Wie erkenne ich, ob eine Python-Variable eine Funktion ist?

685

Ich habe eine Variable xund möchte wissen, ob sie auf eine Funktion zeigt oder nicht.

Ich hatte gehofft, ich könnte so etwas tun:

>>> isinstance(x, function)

Aber das gibt mir:

Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NameError: name 'function' is not defined

Der Grund, warum ich das ausgewählt habe, ist, weil

>>> type(x)
<type 'function'>
Daniel H.
quelle
37
Ich bin durch die Anzahl der Antworten gedrückt , um das Problem arbeiten , indem für einige suchen Anruf Attribut oder aufrufbare Funktion ... Eine saubere Art und Weise ist etwa Typ (a) == types.functionType wie @ Ryan vorgeschlagen
aster
44
@AsTeR Der richtige Weg, um die Eigenschaften von Objekten vom Typ Ente zu überprüfen, besteht darin, sie zu fragen, ob sie quaken, und nicht zu sehen, ob sie in einen Behälter in Entengröße passen. Der Ansatz "Direkt vergleichen" gibt für viele Funktionen, wie z. B. integrierte Funktionen, die falsche Antwort.
John Feminella
3
@ JohnFeminella Während ich Ihnen im Prinzip zustimme. Das OP hat nicht gefragt, ob es aufrufbar ist, nur ob es eine Funktion ist. Vielleicht könnte man argumentieren, dass er eine Unterscheidung zwischen beispielsweise Funktionen und Klassen brauchte?
McKay
3
Für meine Zwecke bin ich hierher gekommen, weil ich es insepct.getsourcefür eine Vielzahl von Objekten verwenden wollte, und es ist eigentlich nicht wichtig, ob das Objekt aufrufbar ist, sondern ob es etwas ist, für das 'Funktion' gegeben ist type(obj). Da Google mich hierher geführt hat, würde ich sagen, dass der Kommentar von AsTeR (für mich) die nützlichste Antwort war. Es gibt viele andere Orte im Internet, die Menschen entdecken __call__oder entdecken können callable.
Tsbertalan
4
@AsTeR Es ist types.FunctionType, mit einem Großbuchstaben F.
Ben Mares

Antworten:

891

Wenn dies für Python 2.x oder Python 3.2+ ist, können Sie auch verwenden callable(). Früher war es veraltet, jetzt ist es nicht mehr veraltet, sodass Sie es wieder verwenden können. Sie können die Diskussion hier lesen: http://bugs.python.org/issue10518 . Sie können dies tun mit:

callable(obj)

Wenn dies für Python 3.x, jedoch vor 3.2 gilt, überprüfen Sie, ob das Objekt ein __call__Attribut hat. Sie können dies tun mit:

hasattr(obj, '__call__')

Der oft vorgeschlagene types.FunctionTypesAnsatz ist nicht korrekt, da er viele Fälle nicht abdeckt, von denen Sie vermutlich möchten, dass er erfolgreich ist, wie bei integrierten Funktionen:

>>> isinstance(open, types.FunctionType)
False

>>> callable(open)
True

Der richtige Weg, um die Eigenschaften von Objekten vom Typ Ente zu überprüfen, besteht darin, sie zu fragen, ob sie quaken, und nicht, ob sie in einen Behälter in Entengröße passen. Verwenden types.FunctionTypeSie es nur, wenn Sie eine ganz bestimmte Vorstellung davon haben, was eine Funktion ist.

John Feminella
quelle
73
Dies sagt Ihnen auch nicht, ob es sich um eine Funktion handelt - nur ob sie aufgerufen werden kann.
Chris B.
23
Hängt von der Anwendung ab, ob die Unterscheidung wichtig ist oder nicht; Ich vermute, Sie haben Recht, dass dies nicht für die ursprüngliche Frage gilt, aber das ist alles andere als sicher.
Chris B.
5
Klassen können eine haben Call - Funktion mit ihm verbunden. Dies ist also definitiv keine gute Methode zur Unterscheidung. Ryans Methode ist besser.
Brian Bruggeman
43
Das Konzept der "Ententypisierung" macht dies zur besseren Antwort, z. B. "Was macht es aus, wenn es sich um eine Funktion handelt, solange sie sich wie eine verhält?"
jcomeau_ictx
8
Es gibt Fälle, in denen die Unterscheidung zwischen einer aufrufbaren und einer Funktion entscheidend ist, beispielsweise beim Schreiben eines Dekorateurs (siehe meinen Kommentar zu Ryans Antwort).
Turion
267

Im Modul befinden sich integrierte Typen, die keine Konstruktoren im integrierten Namespace haben (z. B. Funktionen, Generatoren, Methoden) types. Sie können types.FunctionTypein einem isinstanceAnruf verwenden:

In [1]: import types
In [2]: types.FunctionType
Out[2]: <type 'function'>
In [3]: def f(): pass
   ...:
In [4]: isinstance(f, types.FunctionType)
Out[4]: True
In [5]: isinstance(lambda x : None, types.FunctionType)
Out[5]: True

Beachten Sie, dass hierfür ein sehr spezifischer Begriff von "Funktion" verwendet wird, der normalerweise nicht Ihren Anforderungen entspricht. Zum Beispiel lehnt es ab zip(technisch gesehen eine Klasse):

>>> type(zip), isinstance(zip, types.FunctionType)
(<class 'type'>, False)

open (Eingebaute Funktionen haben einen anderen Typ):

>>> type(open), isinstance(open, types.FunctionType)
(<class 'builtin_function_or_method'>, False)

und random.shuffle(technisch eine Methode einer versteckten random.RandomInstanz):

>>> type(random.shuffle), isinstance(random.shuffle, types.FunctionType)
(<class 'method'>, False)

Wenn Sie etwas Spezifisches für types.FunctionTypeInstanzen tun, z. B. den Bytecode dekompilieren oder Abschlussvariablen untersuchen, verwenden Sie types.FunctionType, aber wenn Sie nur ein Objekt benötigen, das wie eine Funktion aufgerufen werden kann, verwenden Sie callable.

Ryan
quelle
5
+1 Beantwortung der Frage. Der Versuch zu erraten, ob ein Objekt eine Funktion ist - oder ob es sich um ein aufrufbares Objekt handelt - ist normalerweise ein Fehler. Ohne weitere Informationen aus dem OP ist es natürlich schwierig, es
sofort zu verwerfen
47
Es gibt tatsächlich False für eingebaute Funktionen zurück, wie 'open' für z. Um genau zu sein, müssen Sie isinstance (f, (types.FunctionType, types.BuiltinFunctionType)) verwenden. Und natürlich, wenn Sie nur Funktionen wollen, keine aufrufbaren oder Methoden.
Lukasz Korzybski
5
@ ŁukaszKorzybski und um genauer zu sein ... sollten Sie auch nach functools.partial suchen: isinstance(f, (types.FunctionType, types.BuiltinFunctionType, functools.partial))oder f.funcin einem solchen Fall.
Estani
3
@bobince, wie wäre es mit diesem Anwendungsfall: Ich möchte einen Dekorateur schreiben @foo, den ich sowohl als @fooals auch als verwenden kann @foo(some_parameter). Es muss dann überprüfen, mit was es aufgerufen wird, z. B. die zu dekorierende Funktion (erster Fall) oder der Parameter (der zweite Fall, in dem ein weiterer Dekorator zurückgegeben werden muss).
Turion
types.BuiltinFunctionTypeist auch die Art der ("normalen") integrierten Methoden , die Sie wahrscheinlich nicht zulassen möchten, wenn Sie nicht die callableRoute gehen.
user2357112 unterstützt Monica
92

Seit Python 2.1 können Sie isfunctionaus dem inspectModul importieren .

>>> from inspect import isfunction
>>> def f(): pass
>>> isfunction(f)
True
>>> isfunction(lambda x: x)
True
Paolo
quelle
3
Schön, aber es scheint False für eingebaute Funktionen wie openund zurückzugeben hasattr.
Zecc
12
@Zecc isbuiltin ist dafür.
Paolo
13
Siehe inspect.isfunctionDokumentzeichenfolge: "
Mark Mikofski
4
Beachten Sie, dass 'isfunction' functool.partial-Funktionen nicht erkennt.
Ishmael
74

Die akzeptierte Antwort wurde zu dem Zeitpunkt, als sie angeboten wurde, für richtig gehalten. Wie sich herausstellt, gibt es keinen Ersatz für callable()Python 3.2: callable()Überprüft insbesondere das tp_callFeld des zu testenden Objekts. Es gibt kein einfaches Python-Äquivalent. Die meisten der vorgeschlagenen Tests sind meistens korrekt:

>>> class Spam(object):
...     def __call__(self):
...         return 'OK'
>>> can_o_spam = Spam()


>>> can_o_spam()
'OK'
>>> callable(can_o_spam)
True
>>> hasattr(can_o_spam, '__call__')
True
>>> import collections
>>> isinstance(can_o_spam, collections.Callable)
True

Wir können einen Schraubenschlüssel hineinwerfen, indem wir den __call__aus der Klasse entfernen . Und nur um die Dinge besonders aufregend zu halten, fügen Sie __call__der Instanz eine Fälschung hinzu !

>>> del Spam.__call__
>>> can_o_spam.__call__ = lambda *args: 'OK?'

Beachten Sie, dass dies wirklich nicht aufrufbar ist:

>>> can_o_spam()
Traceback (most recent call last):
  ...
TypeError: 'Spam' object is not callable

callable() gibt das richtige Ergebnis zurück:

>>> callable(can_o_spam)
False

Ist hasattraber falsch :

>>> hasattr(can_o_spam, '__call__')
True

can_o_spamhat dieses Attribut doch; Es wird beim Aufrufen der Instanz einfach nicht verwendet.

Noch subtiler, versteht das isinstance()auch falsch:

>>> isinstance(can_o_spam, collections.Callable)
True

Da wir diese Prüfung früher und später verwendet haben, wurde die Methode abc.ABCMeta zwischengespeichert. Das ist wohl ein Fehler abc.ABCMeta. Das heißt, es ist wirklich keine Möglichkeit, es könnte ein genaueres Ergebnis als das Ergebnis produzieren , als durch den Einsatz callable()selbst, da die typeobject->tp_call Schlitzverfahren nicht zugänglich in irgendeiner anderen Art und Weise ist.

Benutz einfach callable()

SingleNegationElimination
quelle
5
Erstaunliche Darstellung der Fallstricke des hasattr(o, '__call__')Ansatzes und warum callable(), falls verfügbar, überlegen ist.
MestreLion
39

Folgendes sollte einen Booleschen Wert zurückgeben:

callable(x)
Nikhil Chelliah
quelle
1
Das löst sein Problem, aber er hat immer noch ein Rätsel geschaffen: Wenn x im eingebauten Modul die Klasse 'Funktion' hat und help (x .__ class__) "Klassenfunktion" beschreibt, warum ist "Funktion" anscheinend "nicht definiert"?
Ken
1
"function" ist kein Schlüsselwort oder integrierter Typ. Der Funktionstyp wird im Modul "types" als "types.FunctionType" definiert
Chris B.
19

callable(x) wird true zurückgeben, wenn das übergebene Objekt in Python aufgerufen werden kann, die Funktion in Python 3.0 jedoch nicht vorhanden ist und im eigentlichen Sinne nicht zwischen folgenden Elementen unterschieden wird:

class A(object):
    def __call__(self):
        return 'Foo'

def B():
    return 'Bar'

a = A()
b = B

print type(a), callable(a)
print type(b), callable(b)

Sie erhalten <class 'A'> Trueund <type function> Trueals Ausgabe.

isinstancefunktioniert perfekt, um festzustellen, ob etwas eine Funktion ist (versuchen isinstance(b, types.FunctionType)); Wenn Sie wirklich daran interessiert sind, ob etwas aufgerufen werden kann, können Sie es entweder verwenden hasattr(b, '__call__')oder einfach ausprobieren.

test_as_func = True
try:
    b()
except TypeError:
    test_as_func = False
except:
    pass

Dies sagt Ihnen natürlich nicht, ob es aufrufbar ist, sondern wirft ein, TypeErrorwenn es ausgeführt wird, oder ist überhaupt nicht aufrufbar. Das ist dir vielleicht egal.

Chris B.
quelle
8
Es zu nennen ist eine schlechte Idee. Was ist, wenn es Nebenwirkungen hat oder tatsächlich etwas tut, aber sehr lange dauert?
Asmeurer
@asmeurer - Warum sollten Sie sonst wissen müssen, ob es sich um eine Funktion handelt, wenn Sie sie nicht aufrufen?
Detly
1
@detly: Zum Debuggen möchte ich regelmäßig alle Variablen in einem Objekt drucken. Die Methoden sind für mich normalerweise nicht nützlich, daher möchte ich sie nicht ausführen. Am Ende liste ich einfach jede nicht aufrufbare Eigenschaft mit den entsprechenden Werten auf :)
Wolph
2
Nur weil Sie nicht anrufen, heißt das nicht, dass es nicht angerufen wird. Vielleicht versendest du.
Asmeurer
4
Es gibt ein großes Problem bei der Verwendung von Ausnahmen, um festzustellen, ob es aufrufbar war oder nicht. was ist, wenn es ist aufrufbar, aber nannte es wirft eine Ausnahme für Sie suchen? Sie werden beide einen Fehler stillschweigend ignorieren und falsch diagnostizieren, ob er aufrufbar war. Wenn Sie EAFP verwenden, möchten Sie wirklich vermeiden, zu viel in den Versuch zu stecken, aber für diesen Anwendungsfall gibt es keine Möglichkeit, dies zu tun.
Ben
15

Wenn Sie alles erkennen möchten, was syntaktisch wie eine Funktion aussieht: eine Funktion, eine Methode, ein integriertes fun / meth, ein Lambda ..., aber aufrufbare Objekte (Objekte mit definierter Methode) ausschließen möchten __call__, versuchen Sie Folgendes:

import types
isinstance(x, (types.FunctionType, types.BuiltinFunctionType, types.MethodType, types.BuiltinMethodType, types.UnboundMethodType))

Ich habe dies mit dem Code der is*()Prüfungen im inspectModul verglichen und der obige Ausdruck ist viel vollständiger, insbesondere wenn Ihr Ziel darin besteht, Funktionen herauszufiltern oder reguläre Eigenschaften eines Objekts zu erkennen.

Marcin Wojnarski
quelle
Vielen Dank, dass Sie mich auf das typesModul hingewiesen haben . Ich habe eine make_stemmer()Factory getestet , die manchmal eine Funktion und manchmal eine aufrufbare StemmerInstanz zurückgibt , und ich musste den Unterschied erkennen.
Kochfelder
7

Versuchen Sie es mit callable(x).

maxyfc
quelle
6

Wenn Sie gelernt haben C++, müssen Sie mit function objectoder vertraut sein, dh mit functorjedem Objekt, das dies kann be called as if it is a function.

Ist in C ++ an ordinary functionein Funktionsobjekt und ein Funktionszeiger. Im Allgemeinen ist dies auch ein Objekt einer Klasse, die definiert operator(). In C ++ 11 und höher the lambda expressionist das functorauch.

Ähnlichkeit, in Python functorssind das alles callable. An ordinary functionkann aufrufbar sein, a lambda expressionkann aufrufbar sein, functional.partialkann aufrufbar sein, die Instanzen von class with a __call__() methodkönnen aufrufbar sein.


Ok, gehe zurück zu Frage: I have a variable, x, and I want to know whether it is pointing to a function or not.

Wenn Sie das Wetter beurteilen möchten, wirkt das Objekt wie eine Funktion, dann ist die von callablevorgeschlagene Methode @John Feminellain Ordnung.

Wenn Sie möchten judge whether a object is just an ordinary function or not(keine aufrufbare Klasseninstanz oder ein Lambda-Ausdruck), ist der xtypes.XXXVorschlag von @Ryaneine bessere Wahl.

Dann mache ich ein Experiment mit diesem Code:

#!/usr/bin/python3
# 2017.12.10 14:25:01 CST
# 2017.12.10 15:54:19 CST

import functools
import types
import pprint

Definieren Sie eine Klasse und eine gewöhnliche Funktion.

class A():
    def __call__(self, a,b):
        print(a,b)
    def func1(self, a, b):
        print("[classfunction]:", a, b)
    @classmethod
    def func2(cls, a,b):
        print("[classmethod]:", a, b)
    @staticmethod
    def func3(a,b):
        print("[staticmethod]:", a, b)

def func(a,b):
    print("[function]", a,b)

Definieren Sie die Funktoren:

#(1.1) built-in function
builtins_func = open
#(1.2) ordinary function
ordinary_func = func
#(1.3) lambda expression
lambda_func  = lambda a : func(a,4)
#(1.4) functools.partial
partial_func = functools.partial(func, b=4)

#(2.1) callable class instance
class_callable_instance = A()
#(2.2) ordinary class function
class_ordinary_func = A.func1
#(2.3) bound class method
class_bound_method = A.func2
#(2.4) static class method
class_static_func = A.func3

Definieren Sie die Funktorenliste und die Typenliste:

## list of functors
xfuncs = [builtins_func, ordinary_func, lambda_func, partial_func, class_callable_instance, class_ordinary_func, class_bound_method, class_static_func]
## list of type
xtypes = [types.BuiltinFunctionType, types.FunctionType, types.MethodType, types.LambdaType, functools.partial]

Beurteilen Sie, ob der Funktor anrufbar ist. Wie Sie sehen können, sind alle abrufbar.

res = [callable(xfunc)  for xfunc in xfuncs]
print("functors callable:")
print(res)

"""
functors callable:
[True, True, True, True, True, True, True, True]
"""

Beurteilen Sie den Typ des Funktors (types.XXX). Dann sind die Arten von Funktoren nicht alle gleich.

res = [[isinstance(xfunc, xtype) for xtype in xtypes] for xfunc in xfuncs]

## output the result
print("functors' types")
for (row, xfunc) in zip(res, xfuncs):
    print(row, xfunc)

"""
functors' types
[True, False, False, False, False] <built-in function open>
[False, True, False, True, False] <function func at 0x7f1b5203e048>
[False, True, False, True, False] <function <lambda> at 0x7f1b5081fd08>
[False, False, False, False, True] functools.partial(<function func at 0x7f1b5203e048>, b=4)
[False, False, False, False, False] <__main__.A object at 0x7f1b50870cc0>
[False, True, False, True, False] <function A.func1 at 0x7f1b5081fb70>
[False, False, True, False, False] <bound method A.func2 of <class '__main__.A'>>
[False, True, False, True, False] <function A.func3 at 0x7f1b5081fc80>
"""

Ich zeichne eine Tabelle mit aufrufbaren Funktortypen unter Verwendung der Daten.

Geben Sie hier die Bildbeschreibung ein

Dann können Sie die geeigneten Funktortypen auswählen.

sowie:

def func(a,b):
    print("[function]", a,b)

>>> callable(func)
True
>>> isinstance(func,  types.FunctionType)
True
>>> isinstance(func, (types.BuiltinFunctionType, types.FunctionType, functools.partial))
True
>>> 
>>> isinstance(func, (types.MethodType, functools.partial))
False
Kinght 金
quelle
6

Als akzeptierte Antwort erklärte John Feminella:

Der richtige Weg, um die Eigenschaften von Objekten vom Typ Ente zu überprüfen, besteht darin, sie zu fragen, ob sie quaken, und nicht, ob sie in einen Behälter in Entengröße passen. Der Ansatz "Direkt vergleichen" gibt für viele Funktionen, wie z. B. integrierte Funktionen, die falsche Antwort.

Obwohl es zwei Bibliotheken gibt, um Funktionen streng zu unterscheiden, zeichne ich eine erschöpfende vergleichbare Tabelle:

8.9. Typen - Dynamische Typerstellung und Namen für integrierte Typen - Python 3.7.0-Dokumentation

30.13. inspizieren - Live-Objekte untersuchen - Python 3.7.0-Dokumentation

#import inspect             #import types
['isabstract',
 'isasyncgen',              'AsyncGeneratorType',
 'isasyncgenfunction', 
 'isawaitable',
 'isbuiltin',               'BuiltinFunctionType',
                            'BuiltinMethodType',
 'isclass',
 'iscode',                  'CodeType',
 'iscoroutine',             'CoroutineType',
 'iscoroutinefunction',
 'isdatadescriptor',
 'isframe',                 'FrameType',
 'isfunction',              'FunctionType',
                            'LambdaType',
                            'MethodType',
 'isgenerator',             'GeneratorType',
 'isgeneratorfunction',
 'ismethod',
 'ismethoddescriptor',
 'ismodule',                'ModuleType',        
 'isroutine',            
 'istraceback',             'TracebackType'
                            'MappingProxyType',
]

Die "Ententypisierung" ist eine bevorzugte Lösung für allgemeine Zwecke:

def detect_function(obj):
    return hasattr(obj,"__call__")

In [26]: detect_function(detect_function)
Out[26]: True
In [27]: callable(detect_function)
Out[27]: True

Wie für die eingebaute Funktion

In [43]: callable(hasattr)
Out[43]: True

Wenn Sie noch einen Schritt weiter gehen, um zu überprüfen, ob die Funktion oder die benutzerdefinierte Funktion integriert ist

#check inspect.isfunction and type.FunctionType
In [46]: inspect.isfunction(detect_function)
Out[46]: True
In [47]: inspect.isfunction(hasattr)
Out[47]: False
In [48]: isinstance(detect_function, types.FunctionType)
Out[48]: True
In [49]: isinstance(getattr, types.FunctionType)
Out[49]: False
#so they both just applied to judge the user-definded

Bestimmen Sie, ob builtin function

In [50]: isinstance(getattr, types.BuiltinFunctionType)
Out[50]: True
In [51]: isinstance(detect_function, types.BuiltinFunctionType)
Out[51]: False

Zusammenfassung

Verwenden Sie, um den callableTyp zu überprüfen und eine Funktion zu überprüfen.
Verwenden types.BuiltinFunctionTypeSie diese Option , wenn Sie einen weiteren Bedarf angegeben haben.

Infinitesimalrechnung
quelle
5

Eine Funktion ist nur eine Klasse mit einer __call__Methode, also können Sie dies tun

hasattr(obj, '__call__')

Zum Beispiel:

>>> hasattr(x, '__call__')
True

>>> x = 2
>>> hasattr(x, '__call__')
False

Das ist die "beste" Methode, aber je nachdem, warum Sie wissen müssen, ob es aufrufbar oder notierbar ist, können Sie es einfach in einen try / execpt-Block einfügen:

try:
    x()
except TypeError:
    print "was not callable"

Es ist fraglich, ob try / exception mehr Python ist als zu tun if hasattr(x, '__call__'): x(). Ich würde sagen, es hasattrist genauer, da Sie nicht versehentlich den falschen TypeError abfangen, zum Beispiel:

>>> def x():
...     raise TypeError
... 
>>> hasattr(x, '__call__')
True # Correct
>>> try:
...     x()
... except TypeError:
...     print "x was not callable"
... 
x was not callable # Wrong!
dbr
quelle
Verwenden Sie die Ausnahmebehandlung nur zum Schutz vor unerwartetem Verhalten, niemals für den Logikfluss - das ist definitiv nicht Pythonic.
Gotgenes
Nun, hasattr macht im Grunde ein getattr in einem try / without-Block (wenn auch in C). blog.jancewicz.net/2007/10/reflection-hasattr.html
dbr
@dbr: Aber hasattr ist ästhetischer.
Nikhil Chelliah
5

Hier sind einige andere Möglichkeiten:

def isFunction1(f) :
    return type(f) == type(lambda x: x);

def isFunction2(f) :
    return 'function' in str(type(f));

So kam ich zum zweiten:

>>> type(lambda x: x);
<type 'function'>
>>> str(type(lambda x: x));
"<type 'function'>"
# Look Maa, function! ... I ACTUALLY told my mom about this!
Sumukh Barve
quelle
Das ist nett! Sollte auf allen Versionen von python2.x und python3.x funktionieren!
Saurav Kumar
4

Statt zu prüfen '__call__'(die Funktionen nicht exklusiv ist), können Sie prüfen , ob eine benutzerdefinierte Funktion Attribute func_name, func_docetc. Das funktioniert nicht für Methoden.

>>> def x(): pass
... 
>>> hasattr(x, 'func_name')
True

Eine andere Möglichkeit zur Überprüfung ist die Verwendung der isfunction()Methode aus dem inspectModul.

>>> import inspect
>>> inspect.isfunction(x)
True

Verwenden Sie, um zu überprüfen, ob ein Objekt eine Methode ist inspect.ismethod()

Stefan van den Akker
quelle
4

Da Klassen auch __call__Methoden haben, empfehle ich eine andere Lösung:

class A(object):
    def __init__(self):
        pass
    def __call__(self):
        print 'I am a Class'

MyClass = A()

def foo():
    pass

print hasattr(foo.__class__, 'func_name') # Returns True
print hasattr(A.__class__, 'func_name')   # Returns False as expected

print hasattr(foo, '__call__') # Returns True
print hasattr(A, '__call__')   # (!) Returns True while it is not a function
Guneysos
quelle
1
stimme deiner Antwort zu, John Feminellas Antwort hasattr(obj, '__call__')ist mehrdeutig.
GoingMyWay
4

Beachten Sie, dass Python-Klassen auch aufgerufen werden können.

Um Funktionen zu erhalten (und mit Funktionen meinen wir Standardfunktionen und Lambdas), verwenden Sie:

import types

def is_func(obj):
    return isinstance(obj, (types.FunctionType, types.LambdaType))


def f(x):
    return x


assert is_func(f)
assert is_func(lambda x: x)
Łukasz Rogalski
quelle
2

Welche Funktion auch immer eine Klasse ist, Sie können den Namen der Klasse der Instanz x nehmen und vergleichen:


if(x.__class__.__name__ == 'function'):
     print "it's a function"
Katsu
quelle
2

Die Lösungen, die in einigen Antworten verwendet hasattr(obj, '__call__')und callable(.)erwähnt werden, haben einen Hauptnachteil: Beide geben auch Truefür Klassen und Instanzen von Klassen mit einer __call__()Methode zurück. Z.B.

>>> import collections
>>> Test = collections.namedtuple('Test', [])
>>> callable(Test)
True
>>> hasattr(Test, '__call__')
True

Eine geeignete Methode, um zu überprüfen, ob ein Objekt eine benutzerdefinierte Funktion ist (und nichts anderes als das), ist die Verwendung von isfunction(.):

>>> import inspect
>>> inspect.isfunction(Test)
False
>>> def t(): pass
>>> inspect.isfunction(t)
True

Wenn Sie nach anderen Typen suchen müssen, schauen Sie unter inspizieren - Live-Objekte untersuchen nach .

Gerald Senarclens de Grancy
quelle
2

Eine genaue Funktionsprüfung

Callable ist eine sehr gute Lösung. Ich wollte dies jedoch anders behandeln als John Feminella. Anstatt es wie folgt zu behandeln:

Der richtige Weg, um die Eigenschaften von Objekten vom Typ Ente zu überprüfen, besteht darin, sie zu fragen, ob sie quaken, und nicht, ob sie in einen Behälter in Entengröße passen. Der Ansatz "Direkt vergleichen" gibt für viele Funktionen, wie z. B. integrierte Funktionen, die falsche Antwort.

Wir werden es so behandeln:

Der richtige Weg, um zu überprüfen, ob es sich bei einer Ente um eine Ente handelt, besteht nicht darin, durch mehrere Filter zu prüfen, ob sie wirklich quaken kann, sondern zu prüfen, ob es sich tatsächlich um eine Ente handelt, anstatt nur zu prüfen, ob sie von der Oberfläche aus wie eine Ente erscheint.

Wie würden wir es umsetzen?

Das Modul 'types' verfügt über zahlreiche Klassen zum Erkennen von Funktionen. Die nützlichsten sind types.FunctionType . Es gibt jedoch auch viele andere Klassen , z. B. einen Methodentyp, einen integrierten Typ und einen Lambda-Typ. Wir werden auch ein 'functools.partial'-Objekt als eine Funktion betrachten.

Die einfache Möglichkeit, zu überprüfen, ob es sich um eine Funktion handelt, besteht darin, für alle diese Typen eine isinstance-Bedingung zu verwenden. Früher wollte ich eine Basisklasse erstellen, die von allen oben genannten Klassen erbt, aber das kann ich nicht, da Python es uns nicht erlaubt, von einigen der oben genannten Klassen zu erben.

Hier ist eine Tabelle, welche Klassen welche Funktionen klassifizieren können:

Funktionstabelle von kinght- 金 Über der Funktionstabelle von kinght- 金

Der Code, der es tut

Dies ist der Code, der alle oben beschriebenen Arbeiten ausführt.

from types import BuiltinFunctionType, BuiltinMethodType,  FunctionType, MethodType, LambdaType
from functools import partial

def is_function(obj):
  return isinstance(obj, (BuiltinFunctionType, BuiltinMethodType,  FunctionType, MethodType, LambdaType, partial))

#-------------------------------------------------

def my_func():
  pass

def add_both(x, y):
  return x + y

class a:
  def b(self):
    pass

check = [

is_function(lambda x: x + x),
is_function(my_func),
is_function(a.b),
is_function(partial),
is_function(partial(add_both, 2))

]

print(check)
>>> [True, True, True, False, True]

Der einzige Fehler war is_function (teilweise), da dies eine Klasse ist, keine Funktion, und dies sind genau Funktionen, keine Klassen. Hier ist eine Vorschau, aus der Sie den Code ausprobieren können.

Fazit

callable (obj) ist die bevorzugte Methode, um zu überprüfen, ob ein Objekt eine Funktion ist, wenn Sie Enten über Absolutes tippen möchten .

Unsere kundenspezifischen is_function (obj) , vielleicht mit einigen Änderungen ist die bevorzugte Methode zu überprüfen , ob ein Objekt eine Funktion ist , wenn Sie nicht aufrufbar Klasseninstanz als Funktion zählen, sondern nur Funktionen definierten integrieren in oder mit Lambda , def oder teilweise .

Und ich denke, das fasst alles zusammen. Haben Sie einen guten Tag!

Corman
quelle
1

In Python3 habe ich mir ausgedacht, type (f) == type (lambda x:x)was ergibt, Trueob fes sich um eine Funktion handelt und Falseob dies nicht der Fall ist. Aber ich denke, ich bevorzuge isinstance (f, types.FunctionType), was sich weniger ad hoc anfühlt. Ich wollte es tun type (f) is function, aber das funktioniert nicht.

Aaron
quelle
0

Nach früheren Antworten habe ich Folgendes gefunden:

from pprint import pprint

def print_callables_of(obj):
    li = []
    for name in dir(obj):
        attr = getattr(obj, name)
        if hasattr(attr, '__call__'):
            li.append(name)
    pprint(li)
Jabba
quelle
0

Sie könnten dies versuchen:

if obj.__class__.__name__ in ['function', 'builtin_function_or_method']:
    print('probably a function')

oder noch etwas bizarreres:

if "function" in lower(obj.__class__.__name__):
    print('probably a function')
Tinnick
quelle
-1

Wenn der Code den Aufruf weiterführt, wenn der Wert aufrufbar ist, führen Sie einfach den Aufruf aus und fangen Sie ab TypeError.

def myfunc(x):
  try:
    x()
  except TypeError:
    raise Exception("Not callable")
Roger Dahl
quelle
4
Das ist gefährlich; Sie haben keine Ahnung, welche Nebenwirkungen xhat.
Cwallenpoole
-2

Das Folgende ist ein "Repr-Weg", um es zu überprüfen. Es funktioniert auch mit Lambda.

def a():pass
type(a) #<class 'function'>
str(type(a))=="<class 'function'>" #True

b = lambda x:x*2
str(type(b))=="<class 'function'>" #True
Vova
quelle
-3

Das funktioniert bei mir:

str(type(a))=="<class 'function'>"
Rafael De Acha
quelle
1
Und was sagt uns das, wenn das Ergebnis eine leere Zeichenfolge ist? Für eine Funktion bekomme ich "<type 'function'>", für eine ganze Zahl bekomme ich "<type 'int'>", also sehe ich nicht, wie es für Sie funktioniert: /
pawamoy
Funktioniert jetzt nur für Python 3 :) Auch abhängig von der ursprünglichen Absicht der Frage wäre es unvollständig: Sollte das eingebaute openals Funktion betrachtet werden? str(type(open))gibt <class 'builtin_function_or_method'>in Python 3.
Pawamoy