ฉันจะตรวจสอบว่าตัวแปร Python เป็นฟังก์ชันได้อย่างไร


687

ฉันมีตัวแปรxและฉันต้องการทราบว่ามันชี้ไปที่ฟังก์ชั่นหรือไม่

ฉันหวังว่าฉันจะทำสิ่งที่ชอบ:

>>> isinstance(x, function)

แต่นั่นทำให้ฉัน:

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

เหตุผลที่ฉันเลือกนั่นเป็นเพราะ

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

37
ฉันรู้สึกหดหู่กับจำนวนคำตอบในการแก้ปัญหาโดยมองหาคุณสมบัติการโทรหรือฟังก์ชั่นการโทรได้ ... วิธีที่ดีคือประเภท (a) == types.functionType ตามที่แนะนำโดย @ryan
AsTeR

44
@AsTeR วิธีที่เหมาะสมในการตรวจสอบคุณสมบัติของวัตถุที่พิมพ์เป็ดคือการถามพวกเขาว่าพวกเขาต้มตุ๋นไม่ดูว่าพวกเขาพอดีในภาชนะขนาดเป็ด วิธีการ "เปรียบเทียบโดยตรง" จะให้คำตอบที่ผิดสำหรับฟังก์ชั่นมากมายเช่นบิวอิน
John Feminella

3
@JohnFeminella ในขณะที่ฉันเห็นด้วยกับคุณในหลักการ OP ไม่ได้ถามว่ามันสามารถใช้งานได้หรือไม่ถ้าเป็นฟังก์ชัน อาจเป็นที่ถกเถียงกันอยู่ว่าเขาต้องการความแตกต่างระหว่างฟังก์ชั่นและคลาส
McKay

3
สำหรับวัตถุประสงค์ของฉันฉันมาที่นี่เพราะผมต้องการที่จะใช้insepct.getsourceในความหลากหลายของวัตถุและเป็นจริงไม่สำคัญหรอกว่าวัตถุเป็น callable แต่ไม่ว่าจะเป็นบางสิ่งบางอย่างที่จะให้ type(obj)'ฟังก์ชั่นสำหรับ ตั้งแต่ google นำฉันมาที่นี่ฉันจะบอกว่าความคิดเห็นของ AsTeR เป็นคำตอบที่มีประโยชน์ที่สุด (สำหรับฉัน) มีมากมายของสถานที่อื่น ๆ บนอินเทอร์เน็ตสำหรับคนที่จะค้นพบมีหรือ__call__ callable
tsbertalan

4
@AsTeR มันเป็นประเภทฟังก์ชันประเภทที่มีทุน F
Ben Mares

คำตอบ:


892

หากนี่เป็นงูหลาม 2.x หรืองูหลาม 3.2+, callable()คุณยังสามารถใช้ มันเคยถูกคัดค้าน แต่ตอนนี้ไม่ได้ถูกยกเลิกดังนั้นคุณจึงสามารถใช้มันได้อีกครั้ง คุณสามารถอ่านการอภิปรายที่นี่: http://bugs.python.org/issue10518 คุณสามารถทำได้ด้วย:

callable(obj)

หากนี่เป็น Python 3.x แต่ก่อน 3.2 ให้ตรวจสอบว่าวัตถุนั้นมี__call__แอตทริบิวต์หรือไม่ คุณสามารถทำได้ด้วย:

hasattr(obj, '__call__')

แนวทางที่แนะนำมักtypes.FunctionTypesไม่ถูกต้องเนื่องจากมันไม่ครอบคลุมหลาย ๆ กรณีที่คุณต้องการให้มันผ่านเหมือนในตัว:

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

>>> callable(open)
True

วิธีที่เหมาะสมในการตรวจสอบคุณสมบัติของวัตถุที่พิมพ์เป็ดคือการถามพวกเขาว่าพวกเขาต้มตุ๋นไม่ดูว่าพวกเขาพอดีในภาชนะขนาดเป็ด อย่าใช้types.FunctionTypeจนกว่าคุณจะมีความคิดที่เฉพาะเจาะจงเกี่ยวกับฟังก์ชั่น


73
สิ่งนี้จะไม่บอกคุณว่ามันเป็นฟังก์ชั่นหรือไม่ถ้ามันสามารถเรียกได้
Chris B.

23
ขึ้นอยู่กับแอพพลิเคชั่นว่ามีความแตกต่างหรือไม่ ฉันสงสัยว่าคุณพูดถูกว่ามันไม่ได้สำหรับคำถามดั้งเดิม แต่ก็ยังห่างไกลจากที่แน่นอน
Chris B.

5
คลาสสามารถมีฟังก์ชั่นการโทรแนบได้ ดังนั้นนี่ไม่ใช่วิธีที่ดีในการแยกแยะ วิธีการของไรอันดีกว่า
Brian Bruggeman

43
แนวคิด "การพิมพ์เป็ด" ทำให้คำตอบนี้ดียิ่งขึ้นเช่น "จะมีความสำคัญอย่างไรหากฟังก์ชันนี้ใช้งานได้นานเท่าที่มันจะทำเช่นนั้น"
jcomeau_ictx

8
มี usecases ที่ความแตกต่างระหว่าง callable และฟังก์ชั่นมีความสำคัญเช่นเมื่อเขียนมัณฑนากร (ดูความคิดเห็นของฉันในคำตอบของ Ryan)
Turion

267

ประเภทบิวอินที่ไม่มีคอนสตรัคเตอร์ในเนมสเปซในตัว (เช่นฟังก์ชั่นเครื่องกำเนิดไฟฟ้าวิธีการ) อยู่ในtypesโมดูล คุณสามารถใช้types.FunctionTypeในการisinstanceโทร:

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

โปรดทราบว่านี่ใช้ความคิดที่เฉพาะเจาะจงมากของ "ฟังก์ชั่น" ที่มักจะไม่ใช่สิ่งที่คุณต้องการ ตัวอย่างเช่นมันปฏิเสธzip(ในทางเทคนิคคลาส):

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

open (ฟังก์ชั่นในตัวมีประเภทที่แตกต่างกัน):

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

และrandom.shuffle(วิธีการทางเทคนิคของrandom.Randomอินสแตนซ์ที่ซ่อนอยู่):

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

หากคุณกำลังทำอะไรบางอย่างที่เฉพาะเจาะจงเพื่อtypes.FunctionTypeกรณีเช่น decompiling bytecode หรือตรวจสอบตัวแปรปิดการใช้งานtypes.FunctionTypeแต่ถ้าคุณเพียงแค่ต้องวัตถุที่จะ callable callableเช่นฟังก์ชั่นการใช้งาน


5
+1 ตอบคำถาม อย่างไรก็ตามการพยายามเดาว่าวัตถุนั้นเป็นฟังก์ชั่นหรือแม้ว่ามันจะเป็นวัตถุที่เรียกได้หรือไม่นั้นก็เป็นความผิดพลาด หากไม่มีข้อมูลเพิ่มเติมจาก OP มันเป็นการยากที่จะยกเลิกมันออกจากมือของหลักสูตร แต่ยัง ...
38417

47
จริง ๆ แล้วมันจะคืนเท็จสำหรับฟังก์ชั่นภายในเช่น 'เปิด' สำหรับเช่น ดังนั้นเพื่อความชัดเจนคุณจะต้องใช้ isinstance (f, (types.FunctionType, types.BuiltinFunctionType)) และแน่นอนถ้าคุณต้องการเพียงแค่ฟังก์ชั่นอย่างเคร่งครัดไม่ใช่ callables หรือวิธีการ
Lukasz Korzybski

5
@ ŁukaszKorzybskiและมีความคิดมากยิ่งขึ้น ... คุณควรตรวจสอบ functools.partial: isinstance(f, (types.FunctionType, types.BuiltinFunctionType, functools.partial))หรือตรวจสอบf.funcในกรณีเช่นนี้
estani

3
@bobince วิธีการเกี่ยวกับ USECASE นี้ผมอยากจะเขียนมัณฑนากร@fooที่ฉันสามารถใช้เป็นทั้งและ@foo @foo(some_parameter)จากนั้นจะต้องตรวจสอบสิ่งที่มันถูกเรียกด้วยเช่นฟังก์ชั่นในการตกแต่ง (กรณีแรก) หรือพารามิเตอร์ (กรณีที่สองซึ่งมันจะต้องกลับไปมัณฑนากรเพิ่มเติม)
Turion

types.BuiltinFunctionTypeนอกจากนี้ยังมีประเภทของ ( "ปกติ") ในตัววิธีการที่คุณอาจไม่ต้องการที่จะอนุญาตให้ถ้าคุณไม่ได้การไปcallableเส้นทาง
user2357112 รองรับ Monica

92

ตั้งแต่ Python 2.1คุณสามารถนำเข้าisfunctionจากinspectโมดูล

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

3
ดี แต่ดูเหมือนว่าจะกลับเท็จสำหรับฟังก์ชั่นในตัวเหมือนและopen hasattr
Zecc

12
@Zecc isbuiltinสำหรับสิ่งนั้น
เปาโล

13
ดูinspect.isfunctiondocstring: "Return จริงถ้าวัตถุเป็นฟังก์ชั่นที่ผู้ใช้กำหนด"
Mark Mikofski

4
โปรดทราบว่า 'isfunction' ไม่รู้จักฟังก์ชัน functool.partial
ishmael

74

คำตอบที่ได้รับการยอมรับคือในเวลาที่คิดว่าถูกต้อง มันจะเปิดออกมีแทนไม่มีสำหรับการcallable()ที่จะกลับมาในหลาม 3.2: โดยเฉพาะcallable()การตรวจสอบtp_callข้อมูลของวัตถุที่ถูกทดสอบ ไม่มีการเทียบเท่า Python ธรรมดา การทดสอบที่แนะนำส่วนใหญ่นั้นถูกต้องเกือบตลอดเวลา:

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

เราสามารถโยนประแจลิงลงในสิ่งนี้ได้โดยการลบออก__call__จากชั้นเรียน และเพื่อให้สิ่งที่น่าตื่นเต้นเป็นพิเศษเพิ่มของปลอม__call__ในอินสแตนซ์!

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

โปรดสังเกตว่านี่ไม่สามารถเรียกได้ว่า:

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

callable() ส่งคืนผลลัพธ์ที่ถูกต้อง:

>>> callable(can_o_spam)
False

แต่hasattrเป็นที่ไม่ถูกต้อง :

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

can_o_spamจะมีคุณลักษณะนั้นหลังจากทั้งหมด; มันไม่ได้ถูกใช้เมื่อเรียกอินสแตนซ์

ที่ลึกซึ้งยิ่งขึ้นisinstance()ก็ผิดเช่นกัน:

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

เนื่องจากเราใช้การตรวจสอบนี้ก่อนหน้านี้และหลังจากนั้นจึงลบวิธีดังกล่าวabc.ABCMeta แคชผลลัพธ์ abc.ABCMetaเนื้อหานี้เป็นข้อผิดพลาดใน ที่กล่าวว่าไม่มีวิธีที่เป็นไปได้จริง ๆ ที่จะสามารถสร้างผลลัพธ์ที่แม่นยำกว่าผลลัพธ์โดยใช้callable()ตัวเองเนื่องจากtypeobject->tp_call วิธีสล็อตไม่สามารถเข้าถึงได้ในวิธีอื่น

เพียงแค่ใช้ callable()


5
ภาพประกอบที่น่าตื่นตาตื่นใจเกี่ยวกับข้อผิดพลาดของhasattr(o, '__call__')วิธีการและทำไมcallable()ถ้ามีก็เหนือกว่า
MestreLion

39

ต่อไปนี้ควรกลับบูลีน:

callable(x)

1
สิ่งนี้แก้ปัญหาของเขาได้ แต่เขายังคงสร้างปริศนา: ถ้า x เป็นฟังก์ชัน 'class' ในโมดูลbuiltinและความช่วยเหลือ (x .__ class__) อธิบายถึง "class function" ทำไม "function" จึงไม่ชัดเจน "?
Ken

1
"ฟังก์ชั่น" ไม่ใช่คำหลักหรือประเภทในตัว ประเภทของฟังก์ชั่นที่กำหนดไว้ในโมดูล "ประเภท" เป็น "types.FunctionType"
Chris B.

25

เครื่องมือ 2to3 ของ Python ( http://docs.python.org/dev/library/2to3.html ) แนะนำ:

import collections
isinstance(obj, collections.Callable)

ดูเหมือนว่านี้ได้รับเลือกแทนการใช้hasattr(x, '__call__')วิธีการเพราะhttp://bugs.python.org/issue7006


1
มันยังกล่าวถึงในรายงานข้อผิดพลาดเกี่ยวกับการนำกลับไปใช้callable()สำหรับ py3.3: bugs.python.org/issue10518#msg122309
luckydonald

19

callable(x) จะกลับจริงถ้าวัตถุที่ส่งผ่านสามารถเรียกได้ใน Python แต่ฟังก์ชั่นไม่มีอยู่ใน Python 3.0 และการพูดอย่างถูกต้องจะไม่แยกความแตกต่างระหว่าง:

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)

คุณจะได้รับ<class 'A'> Trueและ<type function> Trueเป็นผลลัพธ์

isinstanceทำงานได้อย่างสมบูรณ์เพื่อตรวจสอบว่ามีฟังก์ชั่น (ลองisinstance(b, types.FunctionType)) หรือไม่ หากคุณสนใจที่จะรู้ว่าสามารถเรียกใช้บางสิ่งได้คุณสามารถใช้hasattr(b, '__call__')หรือลองใช้ดู

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

แน่นอนว่าสิ่งนี้จะไม่บอกคุณว่ามันสามารถโทรออกได้ แต่จะส่งสัญญาณTypeErrorเมื่อมีการดำเนินการหรือไม่สามารถเริ่มต้นได้ นั่นอาจไม่สำคัญสำหรับคุณ


8
การเรียกมันเป็นความคิดที่ไม่ดี ถ้ามันมีผลข้างเคียงหรือทำอะไรบางอย่าง แต่ใช้เวลานานจริง ๆ ?
asmeurer

@asmeurer - ทำไมคุณต้องรู้อีกว่ามันเป็นฟังก์ชั่นหรือไม่ถ้าไม่โทรหา
Detly

1
@detly: สำหรับการดีบักฉันต้องการพิมพ์ตัวแปรทั้งหมดในวัตถุเป็นประจำวิธีการมักจะไม่มีประโยชน์สำหรับฉันดังนั้นฉันจึงไม่ต้องการที่จะดำเนินการ ในที่สุดฉันเพียงรายการทุกสถานที่ให้บริการที่ไม่ใช่ callable มีค่าที่สอดคล้องกัน :)
Wolph

2
เพียงเพราะคุณไม่ได้โทรมาก็ไม่ได้หมายความว่าไม่ได้โทรมา บางทีคุณกำลังส่งของ
asmeurer

4
มีปัญหาใหญ่ในการใช้ข้อยกเว้นเพื่อทราบว่าสามารถโทรออกได้หรือไม่ ถ้ามันเป็น callable แต่เรียกมันยกข้อยกเว้นคุณกำลังมองหา? คุณทั้งสองจะเพิกเฉยต่อข้อผิดพลาดและวินิจฉัยผิดพลาดว่าโทรออกได้หรือไม่ เมื่อคุณใช้ EAFP คุณต้องการหลีกเลี่ยงการพยายามมากเกินไป แต่ก็ไม่มีวิธีที่จะทำได้สำหรับกรณีการใช้งานนี้
เบ็น

15

หากคุณต้องการตรวจสอบทุกอย่างที่มีลักษณะเหมือน syntactically ฟังก์ชั่น: ฟังก์ชั่นวิธีการสนุก / ปรุงแลมบ์ดา ... แต่ยกเว้นวัตถุ callable (วัตถุที่มี__call__วิธีการกำหนด) จากนั้นลองสิ่งนี้:

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

ฉันเปรียบเทียบสิ่งนี้กับโค้ดis*()เช็คในinspectโมดูลและการแสดงออกด้านบนนั้นสมบูรณ์กว่ามากโดยเฉพาะถ้าเป้าหมายของคุณกรองฟังก์ชันใด ๆ หรือตรวจจับคุณสมบัติปกติของวัตถุ


ขอบคุณที่ชี้นำฉันไปที่typesโมดูล ฉันกำลังทดสอบmake_stemmer()โรงงานที่บางครั้งจะส่งคืนฟังก์ชันและบางครั้งเป็นStemmerอินสแตนซ์ที่เรียกได้และฉันต้องการตรวจสอบความแตกต่าง
เตาแก๊ส


6

ถ้าคุณได้เรียนรู้C++คุณจะต้องคุ้นเคยกับfunction objectหรือfunctorหมายความว่าวัตถุใด ๆ be called as if it is a functionที่สามารถ

ใน C ++ an ordinary functionเป็นวัตถุฟังก์ชันและเป็นตัวชี้ฟังก์ชัน operator()มากกว่าปกติเพื่อให้เป็นวัตถุของคลาสที่กำหนดหนึ่ง ใน C ++ 11 และมากกว่านั้นthe lambda expressionก็functorเช่นกัน

ความคล้ายคลึงกันในหลามผู้ที่มีทั้งหมดfunctors สามารถ callable, callable, a สามารถ callable, อินสแตนซ์ของcallablecallableAn ordinary functiona lambda expressionfunctional.partialclass 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(ไม่ใช่อินสแตนซ์ของคลาส callable หรือนิพจน์แลมบ์ดา) สิ่งที่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]
"""

ตัดสินประเภทของ functor (ประเภท XXX) จากนั้นประเภทของ functors จะไม่เหมือนกันทั้งหมด

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

ฉันวาดตารางประเภท functor callable โดยใช้ข้อมูล

ป้อนคำอธิบายรูปภาพที่นี่

จากนั้นคุณสามารถเลือกประเภทของฟังก์ชั่นที่เหมาะสม

เช่น:

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

ตามคำตอบที่ยอมรับ John Feminella กล่าวว่า:

วิธีที่เหมาะสมในการตรวจสอบคุณสมบัติของวัตถุที่พิมพ์เป็ดคือการถามพวกเขาว่าพวกเขาต้มตุ๋นไม่ดูว่าพวกเขาพอดีในภาชนะขนาดเป็ด วิธีการ "เปรียบเทียบโดยตรง" จะให้คำตอบที่ผิดสำหรับฟังก์ชั่นมากมายเช่นบิวอิน

แม้ว่าจะมีสอง libs เพื่อแยกแยะฟังก์ชั่นอย่างเคร่งครัดฉันวาดตารางเปรียบได้หมดจด:

8.9 types - การสร้างประเภทไดนามิกและชื่อสำหรับประเภทในตัว - เอกสารประกอบของ Python 3.7.0

30.13 inspect - ตรวจสอบวัตถุสด - เอกสาร 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

สำหรับฟังก์ชั่น builtins

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

เมื่อไปอีกหนึ่งขั้นตอนเพื่อตรวจสอบว่าฟังก์ชัน builtin หรือ funtion ที่ผู้ใช้กำหนด

#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ถ้าคุณมีความต้องการที่ระบุเพิ่มเติม


5

ฟังก์ชั่นเป็นเพียงคลาสที่มี__call__เมธอดดังนั้นคุณสามารถทำได้

hasattr(obj, '__call__')

ตัวอย่างเช่น:

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

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

นั่นเป็นวิธีที่ "ดีที่สุด" ในการทำเช่นนั้น แต่ขึ้นอยู่กับสาเหตุที่คุณต้องทราบว่าสามารถโทรออกได้หรือบันทึกย่อคุณสามารถใส่ไว้ในบล็อกลอง / 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!

ใช้การจัดการข้อยกเว้นเพื่อป้องกันพฤติกรรมที่ไม่คาดคิดเท่านั้นไม่ใช่สำหรับโฟลว์ตรรกะ - นั่นไม่ใช่ Pythonic
gotgenes

โดยทั่วไปแล้ว hasattr จะทำ getattr ในบล็อก try / exception (แม้ว่าจะเป็น C) blog.jancewicz.net/2007/10/reflection-hasattr.html
dbr

@dbr: แต่ hasattr นั้นสวยงามกว่า
Nikhil Chelliah

5

นี่เป็นอีกสองวิธี:

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

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

นี่คือวิธีที่ฉันได้รับในครั้งที่สอง:

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

นี่เป็นสิ่งที่ดี! ควรทำงานกับ python2.x และ python3.x ทุกเวอร์ชัน!
Saurav Kumar

4

แทนการตรวจสอบ'__call__'(ซึ่งไม่ได้ จำกัด เฉพาะฟังก์ชั่น) คุณสามารถตรวจสอบว่ามีฟังก์ชั่นที่ผู้ใช้กำหนดมีแอตทริบิวต์func_name, func_docฯลฯ นี้ไม่ทำงานสำหรับวิธีการ

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

อีกวิธีในการตรวจสอบคือการใช้isfunction()วิธีการจากinspectโมดูล

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

เพื่อตรวจสอบว่าวัตถุเป็นวิธีการใช้หรือไม่ inspect.ismethod()


4

เนื่องจากคลาสมี__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

1
เห็นด้วยกับคำตอบของคุณคำตอบของ John Feminella hasattr(obj, '__call__')นั้นไม่ชัดเจน
GoingMyWay

4

โปรดทราบว่าคลาส Python สามารถเรียกใช้ได้เช่นกัน

ในการรับฟังก์ชั่น (และฟังก์ชั่นที่เราหมายถึงฟังก์ชั่นมาตรฐานและ lambdas) ใช้:

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

ฟังก์ชันใดก็ตามที่เป็นคลาสดังนั้นคุณสามารถใช้ชื่อคลาสอินสแตนซ์ x และเปรียบเทียบ:


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

2

โซลูชันที่ใช้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

ตัวตรวจสอบฟังก์ชันที่แน่นอน

callableเป็นทางออกที่ดีมาก อย่างไรก็ตามฉันต้องการปฏิบัติต่อสิ่งนี้ตรงกันข้ามกับ John Feminella แทนที่จะปฏิบัติเช่นนี้:

วิธีที่เหมาะสมในการตรวจสอบคุณสมบัติของวัตถุที่พิมพ์เป็ดคือการถามพวกเขาว่าพวกเขาต้มตุ๋นไม่ดูว่าพวกเขาพอดีในภาชนะขนาดเป็ด วิธีการ "เปรียบเทียบโดยตรง" จะให้คำตอบที่ผิดสำหรับฟังก์ชั่นมากมายเช่นบิวอิน

เราจะปฏิบัติต่อสิ่งนี้:

วิธีที่เหมาะสมในการตรวจสอบว่ามีอะไรที่เป็นเป็ดไม่ได้ดูว่ามันสามารถต้มตุ๋น แต่เพื่อดูว่ามันเป็นเป็ดจริงผ่านตัวกรองหลายตัวแทนที่จะตรวจสอบว่ามันดูเหมือนว่าเป็ดจากพื้นผิว

เราจะใช้มันอย่างไร

โมดูล 'types' มีคลาสจำนวนมากที่ตรวจจับฟังก์ชันได้ซึ่งเป็นประเภทที่มีประโยชน์มากที่สุดFunctionTypeแต่ยังมีอีกหลายประเภทเช่นเมธอดประเภทและแลมบ์ดา นอกจากนี้เรายังจะพิจารณาวัตถุ 'functools.partial' เป็นฟังก์ชัน

วิธีง่ายๆที่เราตรวจสอบว่ามันเป็นฟังก์ชั่นโดยใช้เงื่อนไข isinstance ในทุกประเภทเหล่านี้ ก่อนหน้านี้ฉันต้องการสร้างคลาสพื้นฐานซึ่งสืบทอดมาจากทั้งหมดข้างต้น แต่ฉันไม่สามารถทำได้เนื่องจาก Python ไม่อนุญาตให้เรารับช่วงต่อจากคลาสที่กล่าวมาข้างต้น

นี่คือตารางของคลาสที่สามารถจำแนกฟังก์ชันที่:

ตารางฟังก์ชั่นจาก kinght- 金 ตารางฟังก์ชั่นด้านบนโดย kinght- 金

รหัสที่ทำ

ตอนนี้เป็นรหัสที่ทำงานทั้งหมดที่เราอธิบายจากด้านบน

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]

เท็จหนึ่งคือ is_function (บางส่วน) เพราะนั่นคือชั้นเรียนไม่ใช่ฟังก์ชั่นและนี่คือฟังก์ชั่นที่แน่นอนไม่ใช่ชั้นเรียน นี่คือตัวอย่างสำหรับคุณที่จะลองใช้รหัสจาก

ข้อสรุป

callable (obj)เป็นวิธีที่ต้องการเพื่อตรวจสอบว่าวัตถุเป็นฟังก์ชั่นถ้าคุณต้องการไปโดยการพิมพ์เป็ดผ่านabsolutesตายตัว

เรากำหนดเองis_function (obj)อาจจะมีการแก้ไขบางส่วนเป็นวิธีที่ต้องการตรวจสอบว่าวัตถุที่เป็นฟังก์ชั่นถ้าคุณไม่นับใด ๆ เช่นชั้น callable เป็นหน้าที่ แต่ฟังก์ชั่นเท่านั้นที่กำหนดไว้ในตัวหรือแลมบ์ดา , defหรือบางส่วน

และฉันคิดว่ามันครอบคลุมทั้งหมด ขอให้มีความสุขในวันนี้!


1

ใน Python3 ฉันมาพร้อมกับtype (f) == type (lambda x:x)ที่ให้Trueถ้าfเป็นฟังก์ชั่นและFalseถ้ามันไม่ได้ แต่ฉันคิดว่าฉันชอบisinstance (f, types.FunctionType)ซึ่งรู้สึกเฉพาะกิจน้อยกว่า ฉันอยากจะทำtype (f) is functionแต่มันไม่ได้ผล


0

จากการตอบกลับก่อนหน้านี้ฉันพบสิ่งนี้:

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

คุณสามารถลองสิ่งนี้:

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

หรือแม้แต่สิ่งที่แปลกประหลาดมากขึ้น:

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

-1

ถ้ารหัสจะไปในการที่จะดำเนินการเรียกร้องถ้ามีค่าเป็น callable TypeErrorเพียงดำเนินการโทรและการจับ

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

4
สิ่งนี้เป็นอันตราย คุณไม่รู้ว่าxมีผลข้างเคียงอะไร
cwallenpoole

-2

ต่อไปนี้เป็น "วิธีการพิมพ์ซ้ำ" เพื่อตรวจสอบ มันใช้ได้กับแลมบ์ดาด้วย

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

สิ่งนี้ใช้ได้กับฉัน:

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

1
และนั่นบอกอะไรเราถ้าผลลัพธ์เป็นสตริงว่าง สำหรับฟังก์ชั่นฉันได้รับ"<type 'function'>"เป็นจำนวนเต็มฉันได้"<type 'int'>"ดังนั้นฉันจึงไม่เห็นว่ามันทำงานอย่างไรสำหรับคุณ: /
pawamoy

ตอนนี้ใช้งานได้กับ Python 3 :) นอกจากนี้ยังขึ้นอยู่กับความตั้งใจดั้งเดิมของคำถามมันจะไม่สมบูรณ์: บิวอินในตัวควรopenได้รับการพิจารณาฟังก์ชั่นหรือไม่? str(type(open))ให้<class 'builtin_function_or_method'>ใน Python 3
pawamoy
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.