什么是“可调用”?
现在我们已经搞清楚了元类是什么,但还有一个相关的概念,我一直在用,却不知道它到底是什么意思。
我想大家都曾经因为括号用错而犯过错误,导致出现“对象不可调用”的异常。而且,使用__init__
和__new__
的时候,常常会让人想知道这个让人头疼的__call__
到底是用来干什么的。
你能给我一些解释吗,最好能举些例子来说明这个魔法方法的用法?
13 个回答
可调用对象是指那些可以用圆括号 ( ) 来调用的对象,并且可以传递一些参数,就像函数一样。
每次你定义一个函数时,Python 就会创建一个可调用对象。比如,你可以用以下几种方式定义函数 func(其实是一样的):
class a(object):
def __call__(self, *args):
print 'Hello'
func = a()
# or ...
def func(*args):
print 'Hello'
你可以用这种方式来代替像 doit 或 run 这样的调用方法,我觉得直接用 obj() 看起来更清晰,而不是 obj.doit()。
来自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
。在Python 3.0和3.1中,callable
被移除了,可以用 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')
可调用的东西就是指那些可以被“调用”的东西。
内置的 可调用 检查器(在 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