Как определить, является ли переменная Python функцией? - PullRequest
578 голосов
/ 09 марта 2009

У меня есть переменная x, и я хочу знать, указывает ли она на функцию или нет.

Я надеялся, что смогу сделать что-то вроде:

>>> isinstance(x, function)

Но это дает мне:

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

Причина, по которой я выбрал это, потому что

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

Ответы [ 23 ]

5 голосов
/ 10 декабря 2017

Если вы узнали C++, вы должны быть знакомы с function object или functor, что означает любой объект, который может be called as if it is a function.

В C ++ an ordinary function является функциональным объектом, как и указатель на функцию; в более общем смысле, это объект класса, который определяет operator(). В C ++ 11 и выше the lambda expression - это тоже functor.

Сходство, в Python эти functors все callable. An ordinary function может быть вызвано, a lambda expression может быть вызвано, functional.partial может быть вызвано, экземпляры class with a __call__() method могут быть вызваны.


Хорошо, вернитесь к вопросу: I have a variable, x, and I want to know whether it is pointing to a function or not.

Если вы хотите судить о том, действует ли объект как функция, то метод callable, предложенный @John Feminella, подходит.

Если вы хотите judge whether a object is just an ordinary function or not (не экземпляр вызываемого класса или лямбда-выражение), то xtypes.XXX, предложенный @Ryan, является лучшим выбором.

Затем я делаю эксперимент, используя этот код:

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

import functools
import types
import pprint

Определите класс и обычную функцию.

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)

Определите функторы:

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

Определить список функторов и список типов:

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

Судите, может ли функтор вызываться. Как видите, все они могут быть вызваны.

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

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

Оцените тип функтора (types.XXX). Тогда типы функторов не одинаковы.

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

Я рисую таблицу типов вызываемых функторов, используя данные.

enter image description here

Затем вы можете выбрать подходящие типы функторов.

таких как:

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
5 голосов
/ 09 марта 2009

Функция - это просто класс с методом __call__, так что вы можете сделать

hasattr(obj, '__call__')

Например:

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

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

Это «лучший» способ сделать это, но в зависимости от того, почему вам нужно знать, может ли он быть вызван или записан, вы можете просто поместить его в блок try / execpt:

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

Это спорно, если попробовать / за исключением более Python'y чем делать if hasattr(x, '__call__'): x() .. Я хотел бы сказать hasattr является более точным, так как вы не случайно поймать неправильный TypeError, например:

>>> def x():
...     raise TypeError
... 
>>> hasattr(x, '__call__')
True # Correct
>>> try:
...     x()
... except TypeError:
...     print "x was not callable"
... 
x was not callable # Wrong!
4 голосов
/ 02 сентября 2016

Обратите внимание, что классы Python также могут вызываться.

Чтобы получить функции (под функциями мы подразумеваем стандартные функции и лямбды), используйте:

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)
4 голосов
/ 11 декабря 2014

Поскольку у классов также есть метод __call__, я рекомендую другое решение:

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
4 голосов
/ 11 июля 2018

В качестве принятого ответа Джон Феминелла заявил, что:

Правильный способ проверить свойства объектов типа "утка" - это спросить их, если они крякают, а не посмотреть, помещаются ли они в контейнер размером с утку. Подход «сравните это напрямую» даст неправильный ответ для многих функций, например для встроенных.

Несмотря на то, что есть две библиотеки, чтобы строго различать функции, я рисую исчерпывающую сопоставимую таблицу:

8,9. types - Создание динамических типов и имен для встроенных типов - Документация Python 3.7.0

30,13. осмотреть - Осмотреть живые объекты - Документация 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',
]

«Утиная печать» является предпочтительным решением общего назначения:

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

Что касается встроенной функции

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

Когда еще один шаг, чтобы проверить, встроенная функция или пользовательская функция

#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

Определите, если builtin function

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

Резюме

Используйте callable для проверки типа функции утки,
Используйте types.BuiltinFunctionType, если у вас есть дополнительные требования.

3 голосов
/ 09 сентября 2013

Вместо проверки на '__call__' (которая не является исключительной для функций), вы можете проверить, имеет ли пользовательская функция атрибуты func_name, func_doc и т. Д. Это не работает для методов.

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

Другой способ проверки - использование метода isfunction() из модуля inspect.

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

Чтобы проверить, является ли объект методом, используйте inspect.ismethod()

2 голосов
/ 29 ноября 2013

Решения, использующие hasattr(obj, '__call__') и callable(.), упомянутые в некоторых ответах, имеют главный недостаток: оба также возвращают True для классов и экземпляров классов с методом __call__(). Например.

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

Один из способов проверки того, является ли объект пользовательской функцией (и ничего кроме этого), заключается в использовании isfunction(.):

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

Если вам нужно проверить другие типы, взгляните на осмотреть - осмотреть живые объекты .

2 голосов
/ 24 августа 2013

Какая бы функция ни была классом, вы можете взять имя класса экземпляра x и сравнить:


if(x.__class__.__name__ == 'function'):
     print "it's a function"
1 голос
/ 26 сентября 2011

В Python3 я придумал type (f) == type (lambda x:x), который выдает True, если f является функцией, и False, если нет. Но я думаю, что я предпочитаю isinstance (f, types.FunctionType), который кажется менее специальным. Я хотел сделать type (f) is function, но это не работает.

0 голосов
/ 29 апреля 2019

Это работает для меня:

str(type(a))=="<class 'function'>"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...