Wie erkenne ich, ob eine Variable eine Funktion ist?

Lesezeit: 11 Minuten

Benutzeravatar von Daniel H
Daniel h

Ich habe eine Variable, xund ich möchte wissen, ob es 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'>

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

    – AsTeR

    20. September 2013 um 12:09 Uhr

  • @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.

    – Johannes Feminella

    2. Juni 2014 um 16:58 Uhr

  • @JohnFeminella Wobei ich dir prinzipiell zustimme. Das OP hat nicht gefragt, ob es aufrufbar ist, sondern nur, ob es sich um eine Funktion handelt. Vielleicht könnte man argumentieren, dass er zum Beispiel eine Unterscheidung zwischen Funktionen und Klassen brauchte?

    – McKay

    28. Februar 2017 um 18:15 Uhr

  • Für meine Zwecke bin ich hierher gekommen, weil ich es nutzen wollte insepct.getsource auf eine Vielzahl von Objekten, und es spielt eigentlich keine Rolle, ob das Objekt aufrufbar war, sondern ob es etwas war, das “Funktion” für geben würde type(obj). Da mich Google hierher geführt hat, würde ich sagen, dass der Kommentar von AsTeR die nützlichste Antwort war (für mich). Es gibt viele andere Orte im Internet, die die Leute entdecken können __call__ oder callable.

    – Zbertalan

    7. Dezember 2017 um 21:23 Uhr


  • @ASter Es ist types.FunctionType, mit einem großen F.

    – Ben Stuten

    8. November 2019 um 17:32 Uhr


Benutzeravatar von John Feminella
Johannes Feminella

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

callable(obj)

Wenn dies für Python 3.x, aber vor 3.2 ist, überprüfen Sie, ob das Objekt eine hat __call__ Attribut. Sie können dies tun mit:

hasattr(obj, '__call__')

Das oft suggerierte types.FunctionTypes oder inspect.isfunction Ansatz (beide tun genau dasselbe) enthält eine Reihe von Einschränkungen. Es kehrt zurück False für Nicht-Python-Funktionen. Die meisten eingebaute Funktionensind beispielsweise in C und nicht in Python implementiert, daher kehren sie zurück False:

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

Also types.FunctionType könnte Ihnen überraschende Ergebnisse liefern. Der richtige Weg, die Eigenschaften von entenartigen Objekten zu überprüfen, besteht darin, sie zu fragen, ob sie quaken, und nicht, um zu sehen, ob sie in einen Behälter in Entengröße passen.

  • Dies sagt Ihnen auch nicht, ob es sich um eine Funktion handelt, sondern nur, ob sie aufgerufen werden kann.

    – Chris B.

    9. März 2009 um 4:02 Uhr

  • Hängt von der Anwendung ab, ob die Unterscheidung wichtig ist oder nicht; Ich vermute, Sie haben Recht, dass dies für die ursprüngliche Frage nicht gilt, aber das ist alles andere als sicher.

    – Chris B.

    9. März 2009 um 5:33 Uhr

  • Klassen können a Anruf damit verbundene Funktion. Dies ist also definitiv keine gute Methode zur Unterscheidung. Ryans Methode ist besser.

    – Brian Brüggemann

    2. Dezember 2011 um 20:12 Uhr

  • Das Konzept der “Ententypisierung” macht dies zur besseren Antwort, z. B. “Was spielt es für eine Rolle, ob es sich um eine Funktion handelt, solange sie sich wie eine verhält?”

    – jcomeau_ictx

    2. Januar 2012 um 4:02 Uhr

  • Es gibt Anwendungsfälle, in denen die Unterscheidung zwischen einem aufrufbaren und einer Funktion entscheidend ist, beispielsweise beim Schreiben eines Dekorateurs (siehe meinen Kommentar zu Ryans Antwort).

    – Turi

    6. Dezember 2013 um 22:26 Uhr

Ryans Benutzeravatar
Ryan

Eingebaute Typen, die keine Konstruktoren im eingebauten Namensraum haben (z. B. Funktionen, Generatoren, Methoden), befinden sich in der types Modul. Sie können verwenden types.FunctionType in einem (n isinstance Anruf:

>>> import types
>>> types.FunctionType
<class 'function'>

>>> def f(): pass

>>> isinstance(f, types.FunctionType)
True
>>> isinstance(lambda x : None, types.FunctionType)
True

Beachten Sie, dass dies einen sehr spezifischen Begriff von “Funktion” verwendet, der normalerweise nicht das ist, was Sie brauchen. Sie lehnt zum Beispiel ab zip (technisch 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 gesehen eine Methode eines versteckten random.Random Beispiel):

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

Wenn Sie etwas Bestimmtes tun types.FunctionType Instanzen, wie das Dekompilieren ihres Bytecodes oder das Untersuchen von Closure-Variablen, verwenden types.FunctionTypeaber wenn Sie nur ein Objekt benötigen, das wie eine Funktion aufgerufen werden kann, verwenden Sie callable.

  • +1 Beantwortung der Frage. Der Versuch zu erraten, ob ein Objekt eine Funktion ist – oder sogar ein aufrufbares Objekt – ist normalerweise ein Fehler. Ohne weitere Informationen vom OP ist es natürlich schwierig, es von der Hand zu weisen, aber trotzdem …

    – Bobin

    9. März 2009 um 4:49 Uhr

  • Es wird tatsächlich False für eingebaute Funktionen zurückgeben, wie ‘open’ für zB. Um genau zu sein, müssen Sie isinstance(f, (types.FunctionType, types.BuiltinFunctionType)) verwenden. Und natürlich, wenn Sie nur Funktionen wollen, keine Callables oder Methoden.

    – Lukasz Korzybski

    20. April 2011 um 14:06 Uhr

  • @ŁukaszKorzybski und um genauer zu sein … sollten Sie auch nach functools.partial suchen: isinstance(f, (types.FunctionType, types.BuiltinFunctionType, functools.partial)) oder prüfen f.func in einem solchen Fall.

    – estani

    17. Januar 2013 um 12:04 Uhr

  • @bobince, wie wäre es mit diesem Anwendungsfall: Ich möchte einen Dekorateur schreiben @foo dass ich beides als verwenden kann @foo und wie @foo(some_parameter). Es muss dann prüfen, womit es aufgerufen wird, zB die zu dekorierende Funktion (erster Fall) oder der Parameter (zweiter Fall, in dem es einen weiteren Dekorator zurückgeben muss).

    – Turi

    6. Dezember 2013 um 22:24 Uhr

  • types.BuiltinFunctionType ist auch der typ (“normal”) eingebaut Methodenwas Sie wahrscheinlich nicht zulassen möchten, wenn Sie nicht gehen callable Route.

    – Benutzer2357112

    12. November 2018 um 19:42 Uhr

Benutzeravatar von Paolo
Paolo

Seit Python 2.1 kannst du importieren isfunction von dem inspect Modul.

>>> from inspect import isfunction
>>> def f(): pass
>>> isfunction(f)
True
>>> isfunction(lambda x: x)
True

  • Schön, aber es scheint False für eingebaute Funktionen wie zurückzugeben open und hasattr.

    – Zecc

    4. Mai 2013 um 19:43 Uhr

  • @ Zecc ist eingebaut ist dafür.

    – Paulo

    5. Mai 2013 um 7:16 Uhr

  • Siehe die inspect.isfunction Dokumentstring: “Gib true zurück, wenn das Objekt eine benutzerdefinierte Funktion ist.”

    – Mark Mikofski

    6. August 2013 um 20:27 Uhr


  • Beachten Sie, dass „isfunction“ functool.partial-Funktionen nicht erkennt.

    – Ismael

    5. Dezember 2013 um 19:16 Uhr

  • Das gesamten Quellcode von isfunction ist return isinstance(object, types.FunctionType) es kommt also mit den Einschränkungen, auf die in @Ryans Antwort hingewiesen wird.

    – Boris Werchowskij

    23. Januar 2021 um 21:24 Uhr


Benutzeravatar von SingleNegationElimination
SingleNegationElimination

Die akzeptierte Antwort wurde zum Zeitpunkt des Angebots für richtig gehalten. Wie sich herausstellt, gibt es das kein Ersatz zum callable()das wieder in Python 3.2 enthalten ist: Insbesondere callable() prüft die tp_call Feld des zu testenden Objekts. Es gibt kein einfaches Python-Äquivalent. Die meisten der vorgeschlagenen Tests sind meistens richtig:

>>> 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 die entfernen __call__ aus der Klasse. Und nur um die Dinge besonders spannend zu halten, fügen Sie eine Fälschung hinzu __call__ zur Instanz!

>>> 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

Aber hasattr ist falsch:

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

can_o_spam hat dieses Attribut doch; Es wird nur nicht verwendet, wenn die Instanz aufgerufen wird.

Noch subtiler, isinstance() versteh das auch falsch:

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

Da wir diese Überprüfung früher verwendet und die Methode später gelöscht haben, abc.ABCMeta
speichert das Ergebnis. Vermutlich handelt es sich hier um einen Bug-in abc.ABCMeta. Das heißt, es gibt wirklich keine Möglichkeit könnte produzieren ein genaueres Ergebnis als das Ergebnis als mit callable() selbst, da die typeobject->tp_call
Die Slot-Methode ist auf keine andere Weise zugänglich.

Benutz einfach callable()

Folgendes sollte einen booleschen Wert zurückgeben:

callable(x)

  • Das löst sein Problem, aber er hat immer noch ein Rätsel geschaffen: wenn x in Modul von der Klasse ‘Funktion’ ist eingebautund help(x.__class__) beschreibt “Klassenfunktion”, warum ist “Funktion” anscheinend “nicht definiert”?

    – Ken

    9. März 2009 um 3:52 Uhr

  • „Funktion“ ist kein Schlüsselwort oder ein eingebauter Typ. Der Funktionstyp wird im Modul “types” als “types.FunctionType” definiert.

    – Chris B.

    9. März 2009 um 4:03 Uhr

Benutzeravatar von Lutz Prechelt
Lutz Prechelt

Ergebnis

aufrufbar(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 × × × × × × × × ×
Funk × × × × × × × ×
functools.partial × × × × × × × × × ×
× × × × × × × ×
Generator × × × × × × ×
async_func × × × × × × ×
async_generator × × × × × × ×
EIN × × × × × × × × × × ×
Meth × × × × × × × ×
Klassemeth × × × × × × × × ×
statische Methode × × × × × × × ×
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 meth(self, a, b):
        pass

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

    @staticmethod
    def staticmeth(a, b):
        pass


for func in [print,
             func,
             partial,
             _lambda,
             generator,
             async_func,
             async_generator,
             A,
             A.meth,
             A.classmeth,
             A.staticmeth]:
    judge(func)

Zeit

Wählen Sie die drei gängigsten Methoden:

mal
aufrufbar(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

  • Das löst sein Problem, aber er hat immer noch ein Rätsel geschaffen: wenn x in Modul von der Klasse ‘Funktion’ ist eingebautund help(x.__class__) beschreibt “Klassenfunktion”, warum ist “Funktion” anscheinend “nicht definiert”?

    – Ken

    9. März 2009 um 3:52 Uhr

  • „Funktion“ ist kein Schlüsselwort oder ein eingebauter Typ. Der Funktionstyp wird im Modul “types” als “types.FunctionType” definiert.

    – Chris B.

    9. März 2009 um 4:03 Uhr

Benutzeravatar von Gary van der Merwe
Gary van der Merwe

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

import collections
isinstance(obj, collections.Callable)

Es scheint, dass dies anstelle von gewählt wurde hasattr(x, '__call__') Methode wegen http://bugs.python.org/issue7006.

1436600cookie-checkWie erkenne ich, ob eine Variable eine Funktion ist?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy