Làm cách nào để phát hiện xem biến Python có phải là hàm không?


686

Tôi có một biến, xvà tôi muốn biết liệu nó có trỏ đến một hàm hay không.

Tôi đã hy vọng tôi có thể làm một cái gì đó như:

>>> isinstance(x, function)

Nhưng điều đó mang lại cho tôi:

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

Lý do tôi chọn đó là vì

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

37
Tôi cảm thấy chán nản với số lượng câu trả lời xoay quanh vấn đề bằng cách tìm kiếm một số thuộc tính cuộc gọi hoặc chức năng có thể gọi được ... Một cách rõ ràng là về loại (a) == type.feftType như được đề xuất bởi
@ryan

44
@AsTeR Cách thích hợp để kiểm tra các thuộc tính của các đối tượng gõ vịt là hỏi chúng xem chúng có quăng không, để xem chúng có vừa trong một thùng chứa cỡ vịt không. Phương pháp "so sánh trực tiếp" sẽ đưa ra câu trả lời sai cho nhiều chức năng, như nội dung.
John Women'sella

3
@JohnFeminella Trong khi tôi đồng ý với bạn về nguyên tắc. OP đã không hỏi liệu nó có thể gọi được không, chỉ khi đó là một chức năng. Có lẽ có thể lập luận rằng anh ta cần một sự phân biệt giữa, ví dụ, các chức năng và các lớp?
McKay

3
Với mục đích của tôi, tôi đến đây vì tôi muốn sử dụng insepct.getsourcetrên nhiều đối tượng khác nhau và vấn đề thực sự không phải là liệu đối tượng đó có thể gọi được hay không mà là thứ gì đó sẽ cung cấp 'chức năng' cho type(obj). Vì google dẫn tôi đến đây, tôi muốn nói bình luận của AsTeR là câu trả lời hữu ích nhất (đối với tôi). Có rất nhiều nơi khác trên internet để mọi người khám phá __call__hoặc callable.
tsbertalan

4
@AsTeR Đó là loại.FeftType, với số vốn F.
Ben Mares

Câu trả lời:


891

Nếu đây là cho Python 2.x hoặc cho Python 3.2+, bạn cũng có thể sử dụng callable(). Nó từng bị phản đối, nhưng bây giờ không được đánh giá cao, vì vậy bạn có thể sử dụng lại. Bạn có thể đọc các cuộc thảo luận ở đây: http://bugs.python.org/su10518 . Bạn có thể làm điều này với:

callable(obj)

Nếu đây là cho Python 3.x nhưng trước 3.2, hãy kiểm tra xem đối tượng có __call__thuộc tính không. Bạn có thể làm điều này với:

hasattr(obj, '__call__')

Cách types.FunctionTypestiếp cận được đề xuất là không chính xác vì nó không bao gồm nhiều trường hợp mà bạn có lẽ muốn nó vượt qua, như với nội dung:

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

>>> callable(open)
True

Cách thích hợp để kiểm tra các thuộc tính của các đối tượng gõ vịt là hỏi chúng xem chúng có quậy không, xem chúng có vừa trong một thùng chứa cỡ vịt không. Đừng sử dụng types.FunctionTypetrừ khi bạn có một ý tưởng rất cụ thể về chức năng là gì.


73
Điều này cũng sẽ không cho bạn biết nếu đó là một chức năng - chỉ khi nó có thể được gọi.
Chris B.

23
Phụ thuộc vào ứng dụng cho dù sự khác biệt có quan trọng hay không; Tôi nghi ngờ bạn đúng rằng nó không phù hợp với câu hỏi ban đầu, nhưng điều đó không chắc chắn.
Chris B.

5
Các lớp có thể có một chức năng gọi gắn liền với nó. Vì vậy, đây chắc chắn không phải là một phương pháp tốt để phân biệt. Phương pháp của Ryan là tốt hơn.
Brian Bruggeman

43
khái niệm "gõ vịt" làm cho câu trả lời này tốt hơn, ví dụ "có vấn đề gì nếu nó là một chức năng miễn là nó hoạt động như một?"
jcomeau_ictx

8
Có những giai đoạn mà sự khác biệt giữa chức năng có thể gọi và chức năng là rất quan trọng, ví dụ như khi viết một trang trí (xem nhận xét của tôi về câu trả lời của Ryan).
Turion

267

Các kiểu dựng sẵn không có hàm tạo trong không gian tên dựng sẵn (ví dụ: hàm, trình tạo, phương thức) nằm trong typesmô-đun. Bạn có thể sử dụng types.FunctionTypetrong một isinstancecuộc gọi:

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

Lưu ý rằng điều này sử dụng một khái niệm rất cụ thể về "chức năng" thường không phải là những gì bạn cần. Ví dụ, nó từ chối zip(về mặt kỹ thuật là một lớp):

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

open (các hàm dựng sẵn có một loại khác):

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

random.shuffle(về mặt kỹ thuật là một phương thức của một random.Randomthể hiện ẩn ):

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

Nếu bạn đang làm một cái gì đó cụ thể cho các types.FunctionTypethể hiện, như dịch ngược mã byte của chúng hoặc kiểm tra các biến đóng, hãy sử dụng types.FunctionType, nhưng nếu bạn chỉ cần một đối tượng có thể gọi được như hàm, hãy sử dụng callable.


5
+1 trả lời câu hỏi. Tuy nhiên, cố gắng đoán xem một đối tượng là một chức năng - hay thậm chí nếu đó là bất kỳ đối tượng có thể gọi được - thường là một sai lầm. Nếu không có thêm thông tin từ OP, thật khó để loại bỏ nó khỏi tầm tay, nhưng vẫn ...
bobince

47
Nó thực sự sẽ trả về Sai cho các hàm dựng sẵn, như 'mở' chẳng hạn. Vì vậy, để cụ thể, bạn sẽ phải sử dụng isinstance (f, (type.FeftType, type.BuiltinFunctionType)). Và tất nhiên nếu bạn hoàn toàn chỉ muốn các hàm, không thể gọi được cũng như các phương thức.
Lukasz Korzybski

5
@ UkaszKorzybski và để được ưu tiên hơn ... bạn cũng nên kiểm tra funcools.partial: isinstance(f, (types.FunctionType, types.BuiltinFunctionType, functools.partial))hoặc kiểm tra f.functrong trường hợp như vậy.
estani

3
@bobince, làm thế nào về usecase này: Tôi muốn viết một trang trí @foomà tôi có thể sử dụng cả như @foo@foo(some_parameter). Sau đó, nó cần kiểm tra xem nó đang được gọi là gì, ví dụ hàm để trang trí (trường hợp đầu tiên) hoặc tham số (trường hợp thứ hai, trong đó nó cần trả về một trình trang trí tiếp theo).
Turion

types.BuiltinFunctionTypecũng là loại ( "bình thường") được xây dựng trong phương pháp , mà có thể bạn không muốn cho phép, nếu bạn không đi theo callablelộ trình.
user2357112 hỗ trợ Monica

92

Kể từ Python 2.1, bạn có thể nhập isfunctiontừ inspectmô-đun.

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

3
Đẹp, nhưng nó dường như trả về Sai cho các hàm dựng sẵn như openhasattr.
Zecc

12
@Zecc isbuiltin là dành cho điều đó.
Paolo

13
Xem chuỗi inspect.isfunctiondoc: "Trả về true nếu đối tượng là hàm do người dùng định nghĩa."
Đánh dấu Mikofski

4
Lưu ý rằng 'hàm' không nhận ra các hàm funcool.partial.
ishmael

74

Câu trả lời được chấp nhận là tại thời điểm nó được đưa ra được cho là chính xác. Khi nó quay ra, có gì thay thế cho callable(), mà là trở lại trong Python 3.2: Cụ thể, callable()kiểm tra các tp_calllĩnh vực của đối tượng đang được thử nghiệm. Không có tương đương Python đơn giản. Hầu hết các bài kiểm tra được đề xuất là chính xác hầu hết thời gian:

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

Chúng ta có thể ném một cái mỏ lết vào cái này bằng cách loại bỏ __call__khỏi lớp. Và chỉ để giữ cho mọi thứ thêm thú vị, hãy thêm một __call__ví dụ giả !

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

Lưu ý rằng điều này thực sự không thể gọi được:

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

callable() trả về kết quả đúng:

>>> callable(can_o_spam)
False

Nhưng hasattrsai :

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

can_o_spamkhông có thuộc tính đó sau khi tất cả; nó chỉ không được sử dụng khi gọi ví dụ.

Thậm chí tinh tế hơn, isinstance()cũng nhận được điều này sai:

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

Bởi vì chúng tôi đã sử dụng kiểm tra này trước đó và sau đó đã xóa phương thức, abc.ABCMeta lưu trữ kết quả. Có thể cho rằng đây là một lỗi trong abc.ABCMeta. Điều đó nói rằng, thực sự không có cách nào có thể tạo ra kết quả chính xác hơn kết quả so với sử dụng callable()chính nó, vì typeobject->tp_call phương thức vị trí không thể truy cập theo bất kỳ cách nào khác.

Chỉ dùng callable()


5
Minh họa tuyệt vời về những cạm bẫy của hasattr(o, '__call__')cách tiếp cận và tại sao callable(), nếu có, là vượt trội.
MestreLion

39

Sau đây sẽ trả về một boolean:

callable(x)

1
Điều đó giải quyết vấn đề của anh ta, nhưng anh ta vẫn tạo ra một bí ẩn: nếu x là 'hàm' của lớp được xây dựng trong mô-đun và giúp (x .__ class__) mô tả "hàm lớp", tại sao "hàm" dường như "không được định nghĩa"?
Ken

1
"Hàm" không phải là từ khóa hoặc loại tích hợp. Loại chức năng được xác định trong mô-đun "loại", là "loại.FeftType"
Chris B.


19

callable(x) sẽ trả về true nếu đối tượng được truyền có thể được gọi bằng Python, nhưng hàm không tồn tại trong Python 3.0 và nói đúng sẽ không phân biệt giữa:

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)

Bạn sẽ nhận được <class 'A'> True<type function> Truelà đầu ra.

isinstancehoạt động hoàn toàn tốt để xác định nếu một cái gì đó là một chức năng (thử isinstance(b, types.FunctionType)); nếu bạn thực sự quan tâm đến việc biết nếu một cái gì đó có thể được gọi, bạn có thể sử dụng hasattr(b, '__call__')hoặc chỉ cần thử nó.

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

Điều này, tất nhiên, sẽ không cho bạn biết liệu nó có thể gọi được không nhưng sẽ ném TypeErrorkhi nó thực thi hoặc không thể gọi được ngay từ đầu. Điều đó có thể không quan trọng với bạn.


8
Gọi nó là một ý tưởng tồi. Điều gì nếu nó có tác dụng phụ, hoặc thực sự làm một cái gì đó nhưng mất một thời gian thực sự dài?
asmeker

@asmeker - Tại sao bạn lại cần biết nếu đó là chức năng nếu bạn không gọi nó?
gièm pha

1
@detly: để gỡ lỗi tôi thường xuyên muốn in tất cả các biến trong một đối tượng, các phương thức thường không hữu ích với tôi nên tôi không muốn thực hiện chúng. Cuối cùng, tôi chỉ liệt kê mọi thuộc tính không thể gọi được với các giá trị tương ứng :)
Wolph

2
Chỉ vì bạn không gọi nó không có nghĩa là nó không được gọi. Có lẽ bạn đang làm công văn.
asmeker

4
Có một vấn đề lớn với việc sử dụng các ngoại lệ để biết liệu nó có thể gọi được hay không; Điều gì nếu nó thể gọi được, nhưng gọi nó làm tăng một ngoại lệ mà bạn đang tìm kiếm? Cả hai bạn sẽ âm thầm bỏ qua một lỗi chẩn đoán sai cho dù đó là có thể gọi được. Khi bạn đang sử dụng EAFP, bạn thực sự muốn tránh thử quá nhiều, nhưng không có cách nào để làm điều đó cho trường hợp sử dụng này.
Ben

15

Nếu bạn muốn phát hiện mọi thứ trông giống như một hàm: hàm, phương thức, tích hợp fun / meth, lambda ... nhưng loại trừ các đối tượng có thể gọi được (các đối tượng có __call__phương thức được xác định), thì hãy thử cách này:

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

Tôi đã so sánh điều này với mã is*()kiểm tra trong inspectmô-đun và biểu thức ở trên hoàn chỉnh hơn nhiều, đặc biệt nếu mục tiêu của bạn là lọc ra bất kỳ chức năng nào hoặc phát hiện các thuộc tính thông thường của một đối tượng.


Cảm ơn bạn đã chỉ cho tôi các typesmô-đun. Tôi đã thử nghiệm một make_stemmer()nhà máy đôi khi sẽ trả về một hàm và đôi khi là một thể hiện có thể gọi được Stemmervà tôi cần phát hiện sự khác biệt.
hobs


6

Nếu bạn đã học C++, bạn phải làm quen với function objecthoặcfunctor , có nghĩa là bất kỳ đối tượng nào có thểbe called as if it is a function .

Trong C ++, an ordinary functionlà một đối tượng hàm và con trỏ hàm cũng vậy; nói chung hơn, một đối tượng của một lớp định nghĩa operator(). Trong C ++ 11 trở lên, the lambda expressionfunctor quá.

Tương tự, trong Python, đó functorslà tất cả callable. An ordinary functioncó thể gọi được, a lambda expressioncó thể gọi được, functional.partialcó thể gọi được, các class with a __call__() methodthể hiện có thể gọi được.


Ok, quay trở lại câu hỏi: I have a variable, x, and I want to know whether it is pointing to a function or not.

Nếu bạn muốn phán đoán thời tiết đối tượng hoạt động như một hàm, thì callablephương thức được đề xuất bởi@John Feminella là ok.

Nếu bạn muốn judge whether a object is just an ordinary function or not(không phải là một thể hiện lớp có thể gọi hoặc biểu thức lambda), thì xtypes.XXXđề xuất bởi @Ryanlà một lựa chọn tốt hơn.

Sau đó, tôi làm một thí nghiệm sử dụng các mã đó:

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

import functools
import types
import pprint

Xác định một lớp và một hàm thông thường.

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)

Xác định hàm tử:

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

Xác định danh sách của functor và danh sách các loại:

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

Thẩm phán wunctor là có thể gọi được. Như bạn có thể thấy, tất cả đều có thể gọi được.

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

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

Đánh giá loại functor (loại.XXX). Sau đó, các loại functor không giống nhau.

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

Tôi vẽ một bảng các loại functor có thể gọi được bằng cách sử dụng dữ liệu.

nhập mô tả hình ảnh ở đây

Sau đó, bạn có thể chọn loại functor phù hợp.

nhu la:

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

6

Là câu trả lời được chấp nhận, John Women'sella tuyên bố rằng:

Cách thích hợp để kiểm tra các thuộc tính của các đối tượng gõ vịt là hỏi chúng xem chúng có quậy không, xem chúng có vừa trong một thùng chứa cỡ vịt không. Phương pháp "so sánh trực tiếp" sẽ đưa ra câu trả lời sai cho nhiều chức năng, như nội dung.

Mặc dù, có hai lib để phân biệt các chức năng một cách nghiêm ngặt, tôi vẽ một bảng so sánh đầy đủ:

8,9. loại - Tạo kiểu động và tên cho các loại tích hợp - Tài liệu Python 3.7.0

30,13. kiểm tra - Kiểm tra các đối tượng sống - Tài liệu 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',
]

"Gõ vịt" là một giải pháp ưa thích cho mục đích chung:

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

Đối với hàm dựng sẵn

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

Khi đi thêm một bước nữa để kiểm tra xem hàm dựng sẵn hay chức năng do người dùng định nghĩa

#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

Xác định xem builtin function

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

Tóm lược

Sử dụng callableloại vịt kiểm tra một chức năng,
Sử dụng types.BuiltinFunctionTypenếu bạn có nhu cầu cụ thể hơn.


5

Một hàm chỉ là một lớp với một __call__phương thức, vì vậy bạn có thể làm

hasattr(obj, '__call__')

Ví dụ:

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

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

Đó là cách "tốt nhất" để thực hiện, nhưng tùy thuộc vào lý do tại sao bạn cần biết liệu nó có thể gọi được hay ghi chú hay không, bạn chỉ có thể đặt nó trong một khối try / execpt:

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

Có thể tranh cãi nếu thử / ngoại trừ nhiều Python hơn là làm if hasattr(x, '__call__'): x().. Tôi sẽ nói hasattrlà chính xác hơn, vì bạn sẽ không vô tình bắt nhầm TypeError, ví dụ:

>>> def x():
...     raise TypeError
... 
>>> hasattr(x, '__call__')
True # Correct
>>> try:
...     x()
... except TypeError:
...     print "x was not callable"
... 
x was not callable # Wrong!

Sử dụng xử lý ngoại lệ để chỉ bảo vệ chống lại hành vi bất ngờ, không bao giờ cho luồng logic - đó chắc chắn không phải là Pythonic.
gotgenes

Vâng, về cơ bản hasattr thực hiện một getattr trong một khối thử / ngoại trừ (mặc dù trong C). blog.jancewicz.net/2007/10/reflection-hasattr.html
dbr

@dbr: Nhưng hasattr có tính thẩm mỹ cao hơn.
Nikhil Chelliah

5

Đây là một vài cách khác:

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

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

Đây là cách tôi nghĩ ra thứ hai:

>>> type(lambda x: x);
<type 'function'>
>>> str(type(lambda x: x));
"<type 'function'>"
# Look Maa, function! ... I ACTUALLY told my mom about this!

Cái này đẹp đấy! Nên hoạt động trên tất cả các phiên bản python2.x và python3.x!
Saurav Kumar

4

Thay vì kiểm tra '__call__'(mà không phải là độc quyền cho các chức năng), bạn có thể kiểm tra xem một hàm người dùng định nghĩa có thuộc tính func_name, func_doc, vv Điều này không làm việc cho các phương pháp.

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

Một cách khác để kiểm tra là sử dụng isfunction()phương thức từ inspectmô-đun.

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

Để kiểm tra xem một đối tượng là một phương thức, sử dụng inspect.ismethod()


4

Vì các lớp cũng có __call__phương thức, tôi đề xuất một giải pháp khác:

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

1
đồng ý với câu trả lời của bạn, câu trả lời của John Women'sella hasattr(obj, '__call__')là mơ hồ.
Đi MYWay

4

Lưu ý rằng các lớp Python cũng có thể gọi được.

Để có được chức năng (và theo chức năng, chúng tôi muốn nói đến chức năng tiêu chuẩn và lambdas) sử dụng:

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)

2

Bất kỳ hàm nào là một lớp để bạn có thể lấy tên của lớp của thể hiện x và so sánh:


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

2

Các giải pháp sử dụng hasattr(obj, '__call__')callable(.)được đề cập trong một số câu trả lời có một nhược điểm chính: cả hai cũng trả về Truecho các lớp và thể hiện của các lớp với một __call__()phương thức. Ví dụ.

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

Một cách thích hợp để kiểm tra xem một đối tượng có phải là hàm do người dùng định nghĩa (và không có gì ngoài đó) là sử dụng isfunction(.):

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

Nếu bạn cần kiểm tra các loại khác, hãy xem kiểm tra - Kiểm tra các vật thể sống .


2

Trình kiểm tra chức năng chính xác

có thể gọi là một giải pháp rất tốt. Tuy nhiên, tôi muốn đối xử với điều này theo cách ngược lại của John Women'sella. Thay vì đối xử với nó như câu nói này:

Cách thích hợp để kiểm tra các thuộc tính của các đối tượng gõ vịt là hỏi chúng xem chúng có quậy không, xem chúng có vừa trong một thùng chứa cỡ vịt không. Phương pháp "so sánh trực tiếp" sẽ đưa ra câu trả lời sai cho nhiều chức năng, như nội dung.

Chúng tôi sẽ đối xử với nó như thế này:

Cách thích hợp để kiểm tra xem có gì đó là một con vịt không phải là để xem nó có thể quẫy không, mà là để xem nó có thực sự là một con vịt qua nhiều bộ lọc hay không, thay vì chỉ kiểm tra xem nó có giống như một con vịt từ bề mặt không.

Làm thế nào chúng ta sẽ thực hiện nó

Mô-đun 'loại' có rất nhiều lớp để phát hiện các chức năng, loại hữu dụng nhất là loại.FeftType , nhưng cũng có nhiều loại khác, như kiểu phương thức, kiểu dựng sẵn và kiểu lambda. Chúng tôi cũng sẽ coi một đối tượng 'funcools.partial' là một hàm.

Cách đơn giản chúng tôi kiểm tra xem đó có phải là một hàm hay không bằng cách sử dụng điều kiện đẳng cấp trên tất cả các loại này. Trước đây, tôi muốn tạo một lớp cơ sở kế thừa từ tất cả các mục trên, nhưng tôi không thể làm điều đó, vì Python không cho phép chúng tôi kế thừa từ một số lớp trên.

Đây là bảng các lớp có thể phân loại các chức năng:

Bảng chức năng từ kinght- Bảng chức năng trên của kinght-

Bộ luật nào

Bây giờ, đây là mã thực hiện tất cả các công việc chúng tôi đã mô tả ở trên.

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]

Một sai là is_function (một phần), vì đó là một lớp, không phải là một hàm và đây chính xác là các hàm, không phải các lớp. Đây là một bản xem trước để bạn thử mã từ.

Phần kết luận

callable (obj) là phương thức ưa thích để kiểm tra xem một đối tượng có phải là hàm hay không nếu bạn muốn đi bằng cách gõ vịt trên tuyệt đối .

Tùy chỉnh is_function (obj) của chúng tôi , có thể với một số chỉnh sửa là phương thức ưa thích để kiểm tra xem một đối tượng có phải là hàm hay không nếu bạn không đếm bất kỳ thể hiện lớp có thể gọi nào dưới dạng hàm, nhưng chỉ các hàm được xác định tích hợp hoặc với lambda , def hoặc một phần .

Và tôi nghĩ rằng nó kết thúc tất cả lên. Chúc bạn ngày mới tốt lành!


1

Trong Python3 tôi đã đưa ra type (f) == type (lambda x:x)cái nào mang lại Truenếu flà một hàm và Falsenếu nó không. Nhưng tôi nghĩ rằng tôi thích isinstance (f, types.FunctionType), mà cảm thấy ít quảng cáo. Tôi muốn làm type (f) is function, nhưng điều đó không làm việc.


0

Theo các trả lời trước đó, tôi đã đưa ra điều này:

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)

0

Bạn có thể thử điều này:

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

hoặc thậm chí một cái gì đó kỳ quái hơn:

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

-1

Nếu mã sẽ tiếp tục thực hiện cuộc gọi nếu giá trị có thể gọi được, chỉ cần thực hiện cuộc gọi và bắt TypeError.

def myfunc(x):
  try:
    x()
  except TypeError:
    raise Exception("Not callable")

4
Điều này nguy hiểm; bạn không biết tác dụng phụ của xnó là gì.
cwallenpoole

-2

Sau đây là một "cách repr" để kiểm tra nó. Ngoài ra nó hoạt động với 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

-3

Điều này làm việc cho tôi:

str(type(a))=="<class 'function'>"

1
Và điều đó cho chúng ta biết nếu kết quả là một chuỗi rỗng? Đối với một hàm, tôi nhận được "<type 'function'>", đối với một số nguyên, tôi nhận được "<type 'int'>", vì vậy tôi không thấy nó hoạt động như thế nào đối với bạn: /
pawamoy

Bây giờ chỉ hoạt động cho Python 3 :) Ngoài ra, tùy thuộc vào mục đích ban đầu của câu hỏi, nó sẽ không đầy đủ: có nên coi nội dung trong openmột hàm không? str(type(open))đưa ra <class 'builtin_function_or_method'>trong Python 3.
pawamoy
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.