971 Stimmen

Wie kann ich feststellen, ob eine Python-Variable eine Funktion ist?

Ich habe eine Variable, x und ich möchte wissen, ob sie auf eine Funktion verweist 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

Ich habe das deshalb gewählt, weil

>>> type(x)
<type 'function'>

61 Stimmen

Ich bin deprimiert über die Anzahl der Antworten, die das Problem umgehen, indem sie nach einigen aufrufen Attribut oder aufrufbare Funktion... Ein sauberer Weg ist über type(a) == types.functionType wie von @ryan vorgeschlagen

65 Stimmen

@AsTeR Die richtige Art und Weise, die Eigenschaften von Objekten mit dem Typ "Ente" zu prü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" wird für viele Funktionen, wie z.B. Builtins, die falsche Antwort geben.

5 Stimmen

@JohnFeminella Ich stimme Ihnen zwar im Prinzip zu. Der OP hat nicht gefragt, ob es aufrufbar ist, sondern nur, ob es eine Funktion ist. Vielleicht könnte man argumentieren, dass er eine Unterscheidung zwischen z.B. Funktionen und Klassen benötigt?

40voto

XerCis Punkte 181

Résultat

abrufbar(x)

hasattr(x, '__call__')

inspect.isfunction(x)

inspect.ismethod(x)

inspect.isgeneratorfunction(x)

inspect.iscoroutinefunction(x)

inspect.isasyncgenfunction(x)

isinstance(x, typing.Callable)

isinstance(x, types.BuiltinFunctionType)

isinstance(x, types.BuiltinMethodType)

isinstance(x, types.FunctionType)

isinstance(x, types.MethodType)

isinstance(x, types.LambdaType)

isinstance(x, functools.partial)

drucken

×

×

×

×

×

×

×

×

×

func

×

×

×

×

×

×

×

×

functools.partial

×

×

×

×

×

×

×

×

×

×

<lambda>

×

×

×

×

×

×

×

×

Generator

×

×

×

×

×

×

×

async_func

×

×

×

×

×

×

×

asynchroner_generator

×

×

×

×

×

×

×

A

×

×

×

×

×

×

×

×

×

×

×

func1

×

×

×

×

×

×

×

×

func2

×

×

×

×

×

×

×

×

×

func3

×

×

×

×

×

×

×

×

import types
import inspect
import functools
import typing

def judge(x):
    name = x.__name__ if hasattr(x, '__name__') else 'functools.partial'
    print(name)
    print('\ttype({})={}'.format(name, type(x)))
    print('\tcallable({})={}'.format(name, callable(x)))
    print('\thasattr({}, \'__call__\')={}'.format(name, hasattr(x, '__call__')))
    print()
    print('\tinspect.isfunction({})={}'.format(name, inspect.isfunction(x)))
    print('\tinspect.ismethod({})={}'.format(name, inspect.ismethod(x)))
    print('\tinspect.isgeneratorfunction({})={}'.format(name, inspect.isgeneratorfunction(x)))
    print('\tinspect.iscoroutinefunction({})={}'.format(name, inspect.iscoroutinefunction(x)))
    print('\tinspect.isasyncgenfunction({})={}'.format(name, inspect.isasyncgenfunction(x)))
    print()
    print('\tisinstance({}, typing.Callable)={}'.format(name, isinstance(x, typing.Callable)))
    print('\tisinstance({}, types.BuiltinFunctionType)={}'.format(name, isinstance(x, types.BuiltinFunctionType)))
    print('\tisinstance({}, types.BuiltinMethodType)={}'.format(name, isinstance(x, types.BuiltinMethodType)))
    print('\tisinstance({}, types.FunctionType)={}'.format(name, isinstance(x, types.FunctionType)))
    print('\tisinstance({}, types.MethodType)={}'.format(name, isinstance(x, types.MethodType)))
    print('\tisinstance({}, types.LambdaType)={}'.format(name, isinstance(x, types.LambdaType)))
    print('\tisinstance({}, functools.partial)={}'.format(name, isinstance(x, functools.partial)))

def func(a, b):
    pass

partial = functools.partial(func, a=1)

_lambda = lambda _: _

def generator():
    yield 1
    yield 2

async def async_func():
    pass

async def async_generator():
    yield 1

class A:
    def __call__(self, a, b):
        pass

    def func1(self, a, b):
        pass

    @classmethod
    def func2(cls, a, b):
        pass

    @staticmethod
    def func3(a, b):
        pass

for func in [print,
             func,
             partial,
             _lambda,
             generator,
             async_func,
             async_generator,
             A,
             A.func1,
             A.func2,
             A.func3]:
    judge(func)

Zeit

Wählen Sie die drei gängigsten Methoden

Zeit/s

abrufbar(x)

0.86

hasattr(x, '__call__')

1.36

isinstance(x, typing.Callable)

12.19

import typing
from timeit import timeit

def x():
    pass

def f1():
    return callable(x)

def f2():
    return hasattr(x, '__call__')

def f3():
    return isinstance(x, typing.Callable)

print(timeit(f1, number=10000000))
print(timeit(f2, number=10000000))
print(timeit(f3, number=10000000))
# 0.8643081
# 1.3563508
# 12.193492500000001

0 Stimmen

Schöne Aufschlüsselung! sollte leicht die beste Antwort sein :)

0 Stimmen

Vergessen Sie auch nicht die Tupel-Argument-Version von isinstance. Zum Beispiel: isinstance(x, (types.BuiltinFunctionType, types.BuiltinMethodType, types.FunctionType, types.MethodType, types.LambdaType, functools.partial))

26voto

nh2 Punkte 23111

Das 2to3-Werkzeug von Python ( http://docs.python.org/dev/library/2to3.html ) vorschlägt:

import collections
isinstance(obj, collections.Callable)

Offenbar wurde dies anstelle des hasattr(x, '__call__') Methode wegen der http://bugs.python.org/issue7006 .

1 Stimmen

Es wird auch im Fehlerbericht erwähnt, dass das Zurückbringen von callable() für py3.3: bugs.python.org/issue10518#msg122309

19voto

Chris B. Punkte 78022

callable(x) se gibt true zurück, wenn das übergebene Objekt in Python aufgerufen werden kann, die Funktion aber in Python 3.0 nicht existiert, und genau genommen wird nicht unterschieden zwischen:

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'> True y <type function> True als Ausgabe.

isinstance funktioniert sehr gut, um festzustellen, ob etwas eine Funktion ist (versuchen Sie isinstance(b, types.FunctionType) ); wenn Sie wirklich daran interessiert sind, zu wissen, ob etwas aufgerufen werden kann, können Sie entweder hasattr(b, '__call__') oder probieren Sie es einfach aus.

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

Das sagt Ihnen natürlich nicht, ob es aufrufbar ist, sondern wirft ein TypeError wenn sie ausgeführt wird oder gar nicht erst aufrufbar ist. Das mag für Sie keine Rolle spielen.

9 Stimmen

Sie anzurufen ist eine schlechte Idee. Was ist, wenn es Nebenwirkungen hat oder tatsächlich etwas bewirkt, aber sehr lange dauert?

0 Stimmen

@asmeurer - Warum sollten Sie sonst wissen, ob es eine Funktion ist, wenn Sie sie nicht aufrufen?

1 Stimmen

@detly: Zum Debuggen möchte ich regelmäßig alle Variablen in einem Objekt ausgeben, die Methoden sind für mich normalerweise nicht nützlich, so dass ich sie nicht ausführen möchte. Am Ende liste ich einfach jede nicht aufrufbare Eigenschaft mit den entsprechenden Werten auf :)

17voto

maxyfc Punkte 10827

Versuchen Sie es mit callable(x) .

17voto

Marcin Wojnarski Punkte 1763

Wenn Sie alles erkennen wollen, was syntaktisch wie eine Funktion aussieht: eine Funktion, Methode, eingebaute fun/meth, lambda ... aber ausschließen aufrufbare Objekte (Objekte mit __call__ Methode definiert), dann versuchen Sie es mit dieser:

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

Ich habe dies mit dem Code von is*() checkt ein inspect Modul und der obige Ausdruck ist viel vollständiger, insbesondere wenn Ihr Ziel das Herausfiltern von Funktionen oder das Erkennen von regulären Eigenschaften eines Objekts ist.

1 Stimmen

Vielen Dank für den Hinweis auf die types Modul. Ich habe ein make_stemmer() Fabrik, die manchmal eine Funktion und manchmal eine aufrufbare Datei zurückgibt Stemmer Instanz, und ich musste den Unterschied erkennen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X