Jak wykryć, czy zmienna Python jest funkcją?

687

Mam zmienną xi chcę wiedzieć, czy wskazuje ona na funkcję, czy nie.

Miałem nadzieję, że uda mi się zrobić coś takiego:

>>> isinstance(x, function)

Ale to daje mi:

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

Wybrałem to dlatego, że

>>> type(x)
<type 'function'>
Daniel H.
źródło
37
Jestem przygnębiony liczbą odpowiedzi, które rozwiązują problem, szukając atrybutu wywołania lub funkcji, którą można wywołać ... Czysty sposób dotyczy typu (a) == types.functionType, jak sugeruje @ryan
AsTeR
44
@AsTeR Właściwym sposobem sprawdzenia właściwości obiektów typu kaczka jest zapytanie ich, czy kwakają, a nie sprawdzenie, czy mieszczą się w pojemniku wielkości kaczki. Podejście „porównaj to bezpośrednio” da błędną odpowiedź dla wielu funkcji, takich jak wbudowane.
John Feminella,
3
@JohnFeminella Chociaż zasadniczo zgadzam się z tobą. OP nie zapytał, czy można go wywołać, tylko czy jest to funkcja. Być może można argumentować, że potrzebował rozróżnienia, na przykład, funkcji i klas?
McKay
3
Przybyłem tutaj, ponieważ chciałem używać insepct.getsourcena różnych obiektach, i nie ma znaczenia, czy obiekt można wywoływać, ale czy może to dać „funkcję” type(obj). Ponieważ go tu doprowadziłem, powiedziałbym, że komentarz AsTeR był najbardziej przydatną odpowiedzią (dla mnie). W Internecie znajduje się wiele innych miejsc do odkrycia __call__lub callable.
tsbertalan
4
@AsTeR To types.FunctionType, z dużym F.
Ben Mares

Odpowiedzi:

892

Jeśli dotyczy to Python 2.x lub Python 3.2+, możesz także użyć callable(). Kiedyś był przestarzały, ale teraz jest nieaktualny, więc możesz go użyć ponownie. Możesz przeczytać dyskusję tutaj: http://bugs.python.org/issue10518 . Możesz to zrobić za pomocą:

callable(obj)

Jeśli dotyczy to języka Python 3.x, ale wcześniejszego niż 3.2, sprawdź, czy obiekt ma __call__atrybut. Możesz to zrobić za pomocą:

hasattr(obj, '__call__')

Często sugerowane types.FunctionTypespodejście jest niepoprawne, ponieważ nie obejmuje wielu przypadków, które prawdopodobnie powinny zostać przekazane, tak jak w przypadku wbudowanych:

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

>>> callable(open)
True

Właściwym sposobem sprawdzenia właściwości obiektów typu kaczka jest zapytanie ich, czy kwakają, a nie sprawdzenie, czy mieszczą się w pojemniku wielkości kaczki. Nie używaj, types.FunctionTypechyba że masz bardzo konkretne pojęcie o funkcji.

John Feminella
źródło
73
To również nie powie ci, czy jest to funkcja - tylko czy można ją wywołać.
Chris B.
23
Zależy od wniosku, czy rozróżnienie ma znaczenie, czy nie; Podejrzewam, że masz rację, że nie dotyczy to pierwotnego pytania, ale nie jest to pewne.
Chris B.
5
Klasy mogą mieć dołączoną funkcję wywołania . To zdecydowanie nie jest dobra metoda rozróżniania. Metoda Ryana jest lepsza.
Brian Bruggeman,
43
koncepcja „pisania kaczego” sprawia, że ​​jest to lepsza odpowiedź, np. „co ma znaczenie, jeśli jest to funkcja, o ile zachowuje się jak jedna?”
jcomeau_ictx
8
Istnieją przypadki użycia, w których rozróżnienie między funkcją wywoływaną a funkcją jest kluczowe, na przykład podczas pisania dekoratora (patrz mój komentarz do odpowiedzi Ryana).
Turion,
267

Wbudowane typy, które nie mają konstruktorów we wbudowanej przestrzeni nazw (np. Funkcje, generatory, metody) znajdują się w typesmodule. Można używać types.FunctionTypew isinstancerozmowy:

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

Zauważ, że używa to bardzo specyficznego pojęcia „funkcji”, które zwykle nie jest tym, czego potrzebujesz. Na przykład odrzuca zip(technicznie klasę):

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

open (wbudowane funkcje mają inny typ):

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

oraz random.shuffle(technicznie metoda ukrytej random.Randominstancji):

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

Jeśli robisz coś specyficznego dla types.FunctionTypeinstancji, np. Dekompilujesz ich kod bajtowy lub sprawdzasz zmienne zamknięcia, użyj types.FunctionType, ale jeśli potrzebujesz tylko obiektu, który będzie można wywoływać jak funkcję, użyj callable.

Ryan
źródło
5
+1 odpowiadając na pytanie. Jednak próba odgadnięcia, czy obiekt jest funkcją - a nawet jeśli jest obiektem wywołującym - jest zwykle błędem. Bez dalszych informacji z PO trudno oczywiście odrzucić to z ręki, ale nadal ...
Bobin
47
W rzeczywistości zwróci False dla wbudowanych funkcji, takich jak „open” np. Żeby być konkretnym, będziesz musiał użyć isinstance (f, (types.FunctionType, types.BuiltinFunctionType)). I oczywiście, jeśli chcesz tylko funkcji, a nie metod wywoływania i metod.
Łukasz Korzybski,
5
@ ŁukaszKorzybski i, aby być bardziej prekursorskim ... powinieneś również sprawdzić funools.partial: isinstance(f, (types.FunctionType, types.BuiltinFunctionType, functools.partial))lub sprawdzanie f.funcw takim przypadku.
estani
3
@ Bobince, co powiesz na ten przypadek użycia: Chcę napisać dekorator @foo, którego mogę używać zarówno jako, jak @fooi jako @foo(some_parameter). Następnie musi sprawdzić, co się nazywa, np. Funkcję dekorowania (pierwszy przypadek) lub parametr (drugi przypadek, w którym musi zwrócić kolejny dekorator).
Turion,
types.BuiltinFunctionTypejest także rodzajem („normalnych”) wbudowanych metod , na które prawdopodobnie nie chcesz pozwolić, jeśli nie jedziesz callabletrasą.
user2357112 obsługuje Monikę
92

Od wersji Python 2.1 możesz importować isfunctionz inspectmodułu.

>>> from inspect import isfunction
>>> def f(): pass
>>> isfunction(f)
True
>>> isfunction(lambda x: x)
True
Paolo
źródło
3
Fajnie, ale wydaje się zwracać False dla wbudowanych funkcji takich jak openi hasattr.
Zecc
12
@Zecc isbuiltin jest do tego.
Paolo
13
Zobacz dokumentację inspect.isfunction: „Zwróć wartość true, jeśli obiekt jest funkcją zdefiniowaną przez użytkownika”.
Mark Mikofski,
4
Zauważ, że „isfunction” nie rozpoznaje funkcji funkool.partial.
ishmael,
74

Przyjęta odpowiedź była w chwili, gdy została uznana za poprawną. Jak się okazuje, nie ma substytutu dla callable(), który powraca w Pythonie 3.2: W szczególności callable()sprawdza tp_callpole obiektu badanego. Nie ma zwykłego odpowiednika w języku Python. Większość sugerowanych testów jest poprawna przez większość czasu:

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

Możemy wrzucić do tego klucz małpki, usuwając go __call__z klasy. Żeby było wyjątkowo ekscytująco, dodaj fałszywkę __call__do instancji!

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

Zauważ, że tak naprawdę nie można wywołać:

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

callable() zwraca poprawny wynik:

>>> callable(can_o_spam)
False

Ale hasattrjest źle :

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

can_o_spamma przecież ten atrybut; nie jest po prostu używany podczas wywoływania instancji.

Jeszcze bardziej subtelne, isinstance()również robi to źle:

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

Ponieważ użyliśmy tego sprawdzenia wcześniej, a później usunęliśmy metodę, abc.ABCMeta buforuje wynik. Prawdopodobnie jest to błąd abc.ABCMeta. Powiedział, że tam naprawdę nie możliwy sposób to mogło wytworzyć dokładniejsze wyniki niż wynik niż przy użyciu callable()siebie, ponieważ typeobject->tp_call metoda gniazdo nie jest dostępny w jakikolwiek inny sposób.

Po prostu użyj callable()

SingleNegationElimination
źródło
5
Niesamowita ilustracja pułapek hasattr(o, '__call__')podejścia i dlaczego callable(), jeśli jest dostępna, jest lepsza.
MestreLion,
39

Poniższe powinno zwrócić wartość logiczną:

callable(x)
Nikhil Chelliah
źródło
1
To rozwiązuje jego problem, ale nadal tworzy tajemnicę: jeśli x jest funkcją „klasy” we wbudowanym module , a pomoc (x .__ klasa__) opisuje „funkcję klasy”, to dlaczego „funkcja” najwyraźniej „nie jest zdefiniowana”?
Ken
1
„funkcja” nie jest słowem kluczowym ani typem wbudowanym. Typ funkcji jest zdefiniowany w module „typy” jako „types.FunctionType”
Chris B.
19

callable(x) będzie return true jeśli obiekt przeszedł można nazwać w Pythonie, ale funkcja nie istnieje w Pythonie 3.0, a właściwie mówiąc nie rozróżniają:

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)

Dostaniesz <class 'A'> Truei <type function> Truejako wynik.

isinstancedziała doskonale, aby określić, czy coś jest funkcją (spróbuj isinstance(b, types.FunctionType)); jeśli naprawdę chcesz wiedzieć, czy coś można nazwać, możesz użyć hasattr(b, '__call__')lub po prostu spróbować.

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

To oczywiście nie powie ci, czy jest to możliwe do wywołania, ale rzuca a, TypeErrorkiedy się wykonuje, czy też nie. To może nie mieć znaczenia dla ciebie.

Chris B.
źródło
8
Nazywanie tego to zły pomysł. Co jeśli ma skutki uboczne lub faktycznie coś robi, ale zajmuje to naprawdę dużo czasu?
asmeurer
@asmeurer - Dlaczego miałbyś chcieć wiedzieć, czy jest to funkcja, jeśli jej nie wywołujesz?
detly
1
@detly: do debugowania regularnie chcę wypisywać wszystkie zmienne w obiekcie, metody zwykle nie są dla mnie przydatne, więc nie chciałbym ich wykonywać. Na koniec po prostu wymieniam każdą właściwość, której nie można
wywołać,
2
Tylko dlatego, że jesteś nie nazywając to nie znaczy, że nie jest nazywany. Może robisz wysyłkę.
asmeurer
4
Istnieje duży problem ze stosowaniem wyjątków, aby wiedzieć, czy można je wywołać, czy nie; co jeśli można wywoływać, ale wywołanie go wywołuje wyjątek, którego szukasz? Zarówno po cichu zignorujesz błąd, jak i źle zdiagnozujesz, czy można go wywołać. Podczas korzystania z EAFP naprawdę chcesz uniknąć zbytniego wysiłku, ale nie można tego zrobić w tym przypadku użycia.
Ben
15

Jeśli chcesz wykryć wszystko, co syntaktycznie wygląda jak funkcja: funkcja, metoda, wbudowane fun / met, lambda ... ale wyklucz obiekty, które można wywoływać (obiekty ze __call__zdefiniowaną metodą), spróbuj tego:

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

Porównałem to z kodem is*()kontroli w inspectmodule i powyższe wyrażenie jest znacznie bardziej kompletne, szczególnie jeśli Twoim celem jest odfiltrowanie jakichkolwiek funkcji lub wykrycie regularnych właściwości obiektu.

Marcin Wojnarski
źródło
Dziękuję za skierowanie mnie do typesmodułu. Testowałem make_stemmer()fabrykę, która czasami zwraca funkcję, a czasem Stemmerinstancję na żądanie i musiałem wykryć różnicę.
płyty grzejne
7

Spróbuj użyć callable(x).

maxyfc
źródło
6

Jeśli się nauczyłeś C++, musisz znać function objectlub functoroznacza dowolny obiekt, który może be called as if it is a function.

W C ++ an ordinary functionjest obiektem funkcji, podobnie jak wskaźnik funkcji; bardziej ogólnie, więc jest to obiekt klasy, który definiuje operator(). W C ++ 11 i nowszych the lambda expressionjest to functorsamo.

Podobieństwo w Pythonie functorsto wszystko callable. An ordinary functionmoże być wywoływany, a lambda expressionmoże być wywoływany, functional.partialmoże być wywoływany, przypadki class with a __call__() methodmogą być wywoływane.


Ok, wróć do pytania: I have a variable, x, and I want to know whether it is pointing to a function or not.

Jeśli chcesz ocenić pogodę, obiekt działa jak funkcja, to callablemetoda sugerowana przez @John Feminellajest w porządku.

Jeśli chcesz judge whether a object is just an ordinary function or not(nie wywoływana instancja klasy lub wyrażenie lambda), xtypes.XXXsugerowany przez @Ryanto lepszy wybór.

Następnie wykonuję eksperyment przy użyciu następującego kodu:

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

import functools
import types
import pprint

Zdefiniuj klasę i zwykłą funkcję.

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)

Zdefiniuj funktory:

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

Zdefiniuj listę funktorów i listę typów:

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

Oceniaj, czy funktor jest wywoływalny. Jak widać, wszystkie można wywołać.

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

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

Oceń typ funktora (typy.XXX). Zatem rodzaje funktorów nie są takie same.

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

Rysuję tabelę typów wywoływanych funktorów na podstawie danych.

wprowadź opis zdjęcia tutaj

Następnie możesz wybrać odpowiednie typy funktorów.

Jak na przykład:

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 金
źródło
6

Jako przyjętą odpowiedź John Feminella stwierdził, że:

Właściwym sposobem sprawdzenia właściwości obiektów typu kaczka jest zapytanie ich, czy kwakają, a nie sprawdzenie, czy mieszczą się w pojemniku wielkości kaczki. Podejście „porównaj to bezpośrednio” da błędną odpowiedź dla wielu funkcji, takich jak wbudowane.

Chociaż istnieją dwie biblioteki do ścisłego rozróżnienia funkcji, rysuję wyczerpującą, porównywalną tabelę:

8,9 typy - Dynamiczne tworzenie typów i nazwy dla typów wbudowanych - Dokumentacja Python 3.7.0

30,13. inspekcja - inspekcja obiektów aktywnych - dokumentacja Python 3.7.0

#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',
]

„Pisanie kaczką” jest preferowanym rozwiązaniem ogólnego zastosowania:

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

Jeśli chodzi o funkcję wbudowaną

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

Po przejściu jeszcze jednego kroku, aby sprawdzić, czy funkcja wbudowana lub funkcja zdefiniowana przez użytkownika

#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

Ustal, czy builtin function

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

Podsumowanie

Zatrudniamy callablerodzaju kaczki sprawdzanie funkcji
Zastosowanie types.BuiltinFunctionTypejeśli dalej określone zapotrzebowanie.

Rachunek różniczkowy
źródło
5

Funkcja to tylko klasa z __call__metodą, więc możesz to zrobić

hasattr(obj, '__call__')

Na przykład:

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

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

Jest to „najlepszy” sposób na zrobienie tego, ale w zależności od tego, dlaczego musisz wiedzieć, czy jest to możliwe do wywołania, czy notatki, możesz po prostu umieścić to w bloku try / execpt:

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

Można argumentować, jeśli próba / wyjątek jest bardziej Python'y niż robienie if hasattr(x, '__call__'): x(). Powiedziałbym, że hasattrjest bardziej dokładny, ponieważ przypadkowo nie złapie złego TypeError, na przykład:

>>> def x():
...     raise TypeError
... 
>>> hasattr(x, '__call__')
True # Correct
>>> try:
...     x()
... except TypeError:
...     print "x was not callable"
... 
x was not callable # Wrong!
dbr
źródło
Użyj obsługi wyjątków, aby chronić tylko przed nieoczekiwanym zachowaniem, nigdy dla przepływu logicznego - to zdecydowanie nie jest Pythonic.
gotgenes
No cóż, hasattr w zasadzie robi getattr w bloku try / wyjątkiem (aczkolwiek w C). blog.jancewicz.net/2007/10/reflection-hasattr.html
dbr
@dbr: Ale hasattr jest bardziej estetyczny.
Nikhil Chelliah
5

Oto kilka innych sposobów:

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

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

Oto jak wymyśliłem drugi:

>>> 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
źródło
To jest miłe! Powinien działać na wszystkich wersjach python2.x i python3.x!
Saurav Kumar
4

Zamiast sprawdzania '__call__'(co nie jest wyłącznie do funkcji), można sprawdzić, czy funkcja zdefiniowana przez użytkownika posiada atrybuty func_name, func_docitp to nie działa dla metod.

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

Innym sposobem sprawdzenia jest użycie isfunction()metody z inspectmodułu.

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

Aby sprawdzić, czy obiekt jest metodą, użyj inspect.ismethod()

Stefan van den Akker
źródło
4

Ponieważ klasy mają również __call__metodę, polecam inne rozwiązanie:

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
guneysus
źródło
1
zgadzam się z twoją odpowiedzią, odpowiedź Johna Feminelli hasattr(obj, '__call__')jest niejednoznaczna.
GoingMyWay
4

Zauważ, że klasy Python są również wywoływalne.

Aby uzyskać funkcje (a przez funkcje rozumiemy standardowe funkcje i lambdy) użyj:

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
źródło
2

Jakakolwiek funkcja jest klasą, możesz wziąć nazwę klasy instancji x i porównać:


if(x.__class__.__name__ == 'function'):
     print "it's a function"
Katsu
źródło
2

Rozwiązania wykorzystujące hasattr(obj, '__call__')i callable(.)wspomniane w niektórych odpowiedziach mają główną wadę: oba zwracają się również Truedo klas i instancji klas za pomocą __call__()metody. Na przykład.

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

Jednym z prawidłowych sposobów sprawdzenia, czy obiekt jest funkcją zdefiniowaną przez użytkownika (i nic poza tym), jest użycie isfunction(.):

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

Jeśli chcesz sprawdzić inne typy, spójrz na inspekcja - Sprawdź obiekty na żywo .

Gerald Senarclens de Grancy
źródło
2

Dokładny kontroler funkcji

callable to bardzo dobre rozwiązanie. Chciałem jednak potraktować to w odwrotny sposób niż John Feminella. Zamiast traktować to w ten sposób, mówiąc:

Właściwym sposobem sprawdzenia właściwości obiektów typu kaczka jest zapytanie ich, czy kwakają, a nie sprawdzenie, czy mieszczą się w pojemniku wielkości kaczki. Podejście „porównaj to bezpośrednio” da błędną odpowiedź dla wielu funkcji, takich jak wbudowane.

Potraktujemy to w następujący sposób:

Właściwym sposobem sprawdzenia, czy coś jest kaczką, nie jest sprawdzenie, czy może kwakać, ale raczej sprawdzenie, czy naprawdę jest to kaczka przez kilka filtrów, zamiast po prostu sprawdzić, czy wydaje się, że jest kaczką z powierzchni.

Jak moglibyśmy to wdrożyć

Moduł „typów” ma wiele klas do wykrywania funkcji, z których najbardziej użyteczne są typy , ale istnieje również wiele innych, takich jak typ metody, typ wbudowany i typ lambda. Rozważymy również obiekt „funkools.partial” jako funkcję.

Prostym sposobem sprawdzenia, czy jest to funkcja, jest użycie warunku isinstance na wszystkich tych typach. Wcześniej chciałem stworzyć klasę bazową, która dziedziczy po wszystkich powyższych, ale nie jestem w stanie tego zrobić, ponieważ Python nie pozwala nam dziedziczyć po niektórych z powyższych klas.

Oto tabela klas, które mogą klasyfikować funkcje:

Tabela funkcji z kinght- 金 Powyższa tabela funkcji autorstwa kinght- 金

Kod, który to robi

To jest kod, który wykonuje całą pracę opisaną powyżej.

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]

Jedynym fałszem było is_function (częściowe), ponieważ to jest klasa, a nie funkcja, i to są dokładnie funkcje, a nie klasy. Oto podgląd, z którego możesz wypróbować kod.

Wniosek

callable (obj) jest preferowaną metodą sprawdzania, czy obiekt jest funkcją, jeśli chcesz przejść przez pisanie kaczką ponad absolutami .

Nasza niestandardowa funkcja is_funkcja (obj) , być może z niektórymi zmianami jest preferowaną metodą sprawdzania, czy obiekt jest funkcją, jeśli nie policzymy instancji klasy wywoływalnej jako funkcji, ale tylko funkcje zdefiniowane jako wbudowane lub z lambda , def lub częściowe .

I myślę, że to wszystko podsumowuje. Miłego dnia!

Corman
źródło
1

W Python3 wymyśliłem, type (f) == type (lambda x:x)który daje, Truejeśli fjest funkcją, a Falsejeśli nie jest. Ale myślę, że wolę isinstance (f, types.FunctionType), co wydaje się mniej ad hoc. Chciałem to zrobić type (f) is function, ale to nie działa.

Aaron
źródło
0

Po poprzednich odpowiedziach wymyśliłem to:

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
źródło
0

Możesz spróbować:

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

lub nawet coś bardziej dziwnego:

if "function" in lower(obj.__class__.__name__):
    print('probably a function')
blaszanka
źródło
-1

Jeśli kod będzie kontynuował wykonywanie połączenia, jeśli wartość jest możliwa do wywołania, po prostu wykonaj połączenie i złap TypeError.

def myfunc(x):
  try:
    x()
  except TypeError:
    raise Exception("Not callable")
Roger Dahl
źródło
4
To jest niebezpieczne; nie masz pojęcia, jakie są skutki uboczne x.
cwallenpoole,
-2

Poniżej znajduje się „sposób powtórzenia”, aby to sprawdzić. Działa również z 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
Wowa
źródło
-3

To działa dla mnie:

str(type(a))=="<class 'function'>"
Rafael De Acha
źródło
1
Co to mówi nam, jeśli wynikiem jest pusty ciąg? Dla funkcji dostaję "<type 'function'>", dla liczby całkowitej, "<type 'int'>"więc nie rozumiem, jak to działa dla ciebie: /
pawamoy
Teraz działa tylko dla Python 3 :) Również w zależności od pierwotnej intencji pytania byłoby niekompletne: czy wbudowaną openfunkcję należy uznać za funkcję? str(type(open))daje <class 'builtin_function_or_method'>w Pythonie 3.
pawamoy