现在,元类是什么已经很清楚了,有一个相关的概念,我一直在使用,但不知道它的真正含义。

我想每个人都犯过一次圆括号错误,导致“对象不可调用”异常。更重要的是,使用__init__和__new__会导致想知道这个该死的__call__可以用来做什么。

你能给我一些解释吗,包括魔术方法的例子?


当前回答

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

其他回答

可调用对象是任何可以调用的对象。

内置可调用对象(objects.c中的PyCallable_Check)检查参数是否为:

类的实例,具有__call__方法或 具有非空tp_call (c struct)成员的类型,该成员指示可调用性(例如在函数、方法等中)。

名为__call__的方法是(根据文档)

当实例作为函数被“调用”时调用

例子

class Foo:
  def __call__(self):
    print 'called'

foo_instance = Foo()
foo_instance() #this is calling the __call__ method

很简单,“可调用对象”是可以像方法一样调用的对象。内置函数“callable()”将告诉您某些东西是否看起来是可调用的,就像检查call属性一样。函数和类一样都是可调用的,类实例也是可调用的。看这里和这里。

来自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可调用iff x->ob_type->tp_call != NULL

tp_call字段说明:

ternaryfunc tp_call可选 指向实现的函数的指针 调用对象。这应该是 如果对象不可调用,则为NULL。 签名与for相同 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')

让我反过来解释:

考虑这个……

foo()

... 作为语法上的糖:

foo.__call__()

其中foo可以是任何响应__call__的对象。当我说任何对象时,我指的是内置类型,你自己的类和它们的实例。

对于内置类型,当你写:

int('10')
unicode(10)

你实际上是在做:

int.__call__('10')
unicode.__call__(10)

这也是Python中没有foo = new int的原因:你只需要让类对象在__call__上返回它的一个实例。在我看来,Python解决这个问题的方式非常优雅。

具有__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

然后,只有没有__call__()的Class2对象是不可调用的,返回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