什么是“可调用”?
- 2024-12-02 08:41:00
- admin 原创
- 179
问题描述:
现在已经清楚了什么是元类,有一个相关概念我一直使用但不知道它真正的含义。
我想每个人都曾经犯过使用括号的错误,导致出现“对象不可调用”异常。更重要的是,使用__init__
和__new__
会导致想知道这个该死的__call__
能用来做什么。
你能给我一些解释吗,包括使用魔法方法的例子?
解决方案 1:
可调用对象是指任何能够被调用的东西。
内置可调用函数(objects.c 中的 PyCallable_Check)检查参数是否为:
具有方法的类的实例
__call__
或是一种具有非空tp_call (c 结构)成员的类型,该成员指示可调用性(例如在函数、方法等中)
命名的方法__call__
是(根据文档)
当实例作为函数被“调用”时调用
例子
class Foo:
def __call__(self):
print 'called'
foo_instance = Foo()
foo_instance() #this is calling the __call__ method
解决方案 2:
来自Python的源代码object.c:
/* Test whether an object can be called */
int
PyCallable_Check(PyObject *x)
{
if (x == NULL)
return 0;
if (PyInstance_Check(x)) {
PyObject *call = PyObject_GetAttrString(x, "__call__");
if (call == NULL) {
PyErr_Clear();
return 0;
}
/* Could test recursively but don't, for fear of endless
recursion if some joker sets self.__call__ = self */
Py_DECREF(call);
return 1;
}
else {
return x->ob_type->tp_call != NULL;
}
}
它说:
如果一个对象是某个类的实例,那么当且仅当它具有属性,
__call__
它才是可调用的。否则该对象
x
可调用当且仅当x->ob_type->tp_call != NULL
tp_call
字段描述:
ternaryfunc tp_call
指向实现调用对象的函数的可选指针。如果对象不可调用,则应为 NULL。签名与 PyObject_Call() 相同。此字段由子类型继承。
您始终可以使用内置callable
函数来确定给定对象是否可调用;或者更好的是,直接调用它并TypeError
稍后捕获。callable
在 Python 3.0 和 3.1 中被删除,请使用callable = lambda o: hasattr(o, '__call__')
或isinstance(o, collections.Callable)
。
例如,一个简单的缓存实现:
class Cached:
def __init__(self, function):
self.function = function
self.cache = {}
def __call__(self, *args):
try: return self.cache[args]
except KeyError:
ret = self.cache[args] = self.function(*args)
return ret
用法:
@Cached
def ack(x, y):
return ack(x-1, ack(x, y-1)) if x*y else (x + y + 1)
标准库中的示例,文件site.py
,内置exit()
和quit()
函数的定义:
class Quitter(object):
def __init__(self, name):
self.name = name
def __repr__(self):
return 'Use %s() or %s to exit' % (self.name, eof)
def __call__(self, code=None):
# Shells like IDLE catch the SystemExit, but listen when their
# stdin wrapper is closed.
try:
sys.stdin.close()
except:
pass
raise SystemExit(code)
__builtin__.quit = Quitter('quit')
__builtin__.exit = Quitter('exit')
解决方案 3:
可调用对象允许您使用圆括号()并最终传递一些参数,就像函数一样。
每次定义函数时,python 都会创建一个可调用对象。例如,您可以按以下方式定义函数func(相同):
class a(object):
def __call__(self, *args):
print 'Hello'
func = a()
# or ...
def func(*args):
print 'Hello'
您可以使用此方法,而不是doit或run之类的方法,我认为 obj() 比 obj.doit() 更清晰
解决方案 4:
让我反过来解释一下:
考虑一下这个...
foo()
... 作为语法糖:
foo.__call__()
其中foo
可以是任何响应的对象__call__
。当我说任何对象时,我指的是:内置类型、您自己的类及其实例。
对于内置类型,当你编写:
int('10')
unicode(10)
你实际上是在做:
int.__call__('10')
unicode.__call__(10)
这也是为什么在 Python 中不需要foo = new int
:你只需让类对象在 上返回它的一个实例即可__call__
。我认为 Python 解决这个问题的方式非常优雅。
解决方案 5:
__call__
使任何对象都可以作为函数调用。
此示例将输出 8:
class Adder(object):
def __init__(self, val):
self.val = val
def __call__(self, val):
return self.val + val
func = Adder(5)
print func(3)
解决方案 6:
Callable 是具有__call__
方法的对象。这意味着您可以伪造可调用函数或做一些巧妙的事情,例如部分函数应用,即获取一个函数并添加一些内容来增强它或填充一些参数,然后返回可以依次调用的内容(在函数式编程圈中称为Currying )。
某些输入错误会导致解释器尝试调用您不想要的内容,例如字符串。当解释器尝试执行不可调用的应用程序时,这可能会产生错误。您可以通过执行类似下面的脚本在 Python 解释器中看到这种情况。
[nigel@k9 ~]$ python
Python 2.5 (r25:51908, Nov 6 2007, 15:55:44)
[GCC 4.1.2 20070925 (Red Hat 4.1.2-27)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 'aaa'() # <== Here we attempt to call a string.
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object is not callable
>>>
解决方案 7:
简单来说,“可调用”是指可以像方法一样调用的东西。内置函数“callable()”会告诉您某事物是否可调用,并检查调用属性。函数是可调用的,类也是可调用的,类实例也是可调用的。有关更多信息,请参见此处和此处。
解决方案 8:
在 Python 中,可调用对象是一种具有以下方法的对象__call__
:
>>> class Foo:
... pass
...
>>> class Bar(object):
... pass
...
>>> type(Foo).__call__(Foo)
<__main__.Foo instance at 0x711440>
>>> type(Bar).__call__(Bar)
<__main__.Bar object at 0x712110>
>>> def foo(bar):
... return bar
...
>>> type(foo).__call__(foo, 42)
42
就这么简单:)
这当然可以被重载:
>>> class Foo(object):
... def __call__(self):
... return 42
...
>>> f = Foo()
>>> f()
42
解决方案 9:
你可以将“(args)”放在它后面并期望它能工作。可调用函数通常是一种方法或一个类。方法被调用,类被实例化。
解决方案 10:
检查类的函数或方法是否可调用,意味着我们可以调用该函数。
Class A:
def __init__(self,val):
self.val = val
def bar(self):
print "bar"
obj = A()
callable(obj.bar)
True
callable(obj.__init___)
False
def foo(): return "s"
callable(foo)
True
callable(foo())
False
解决方案 11:
可调用对象实现了__call__
特殊方法,因此任何具有此类方法的对象都是可调用的。
解决方案 12:
Callable 是具有方法调用的“内置函数或方法”的类型或类
>>> type(callable)
<class 'builtin_function_or_method'>
>>>
例如:
print是一个可调用对象。使用内置函数调用
调用print函数时,Python 会创建一个print 类型的对象并调用其方法调用,并传递参数(如果有)。
>>> type(print)
<class 'builtin_function_or_method'>
>>> print.__call__(10)
10
>>> print(10)
10
>>>
解决方案 13:
__call__()
具有可调用性的类、函数、方法和对象。
您可以使用callable()检查是否可以调用,如果可调用则返回True
,如果不可调用则返回,False
如下所示:
class Class1:
def __call__(self):
print("__call__")
class Class2:
pass
def func():
pass
print(callable(Class1)) # Class1
print(callable(Class2)) # Class2
print(callable(Class1())) # Class1 object
print(callable(Class2())) # Class2 object
print(callable(func)) # func
然后,只有没有可调用的Class2
对象__call__()
才返回,False
如下所示:
True # Class1
True # Class2
True # Class1 object
False # Class2 object
True # func
此外,下面所有这些都不是可调用返回的,False
如下所示:
print(callable("Hello")) # "str" type
print(callable(100)) # "int" type
print(callable(100.23)) # "float" type
print(callable(100 + 2j)) # "complex" type
print(callable(True)) # "bool" type
print(callable(None)) # "NoneType"
print(callable([])) # "list" type
print(callable(())) # "tuple" type
print(callable({})) # "dict" type
print(callable({""})) # "set" type
输出:
False # "str" type
False # "int" type
False # "float" type
False # "complex" type
False # "bool" type
False # "NoneType"
False # "list" type
False # "tuple" type
False # "dict" type
False # "set" type