什么是“可赎回”?


310

既然清楚了什么是元类,就有一个相关的概念,我一直都在使用它,而不知道它的真正含义。

我想每个人都曾因括号错误而导致“对象不可调用”异常。而且,使用__init____new__导致怀疑这种血腥的__call__用途。

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


Answers:


308

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

所述内置的可调用(PyCallable_Check在objects.c)检查该参数可以是:

  • 具有__call__方法的类的实例或
  • 具有非null tp_call(c struct)成员的类型,该成员以其他方式指示可调用性(例如在函数,方法等中)

命名的方法__call__是(根据文档

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

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

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

6
请注意,在Python 3.0中已删除了内置的callable以便检查呼叫
Eli Courtwright

13
@Eli:嗯,这听起来不好。callable实际告诉您某些东西是否可调用,而进行检查则__call__告诉您什么都没有;如果一个对象o提供__getattribute____getattr__,则hasattr(o, '__call__')可能返回True,但o仍将不可调用,因为Python会跳过__getattribute____getattr__进行调用。EAFP是唯一剩下的检查某项是否可调用的实际方法。
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳

48
@Longpoke:仅作记录,请参阅Python 3.x中的文档callable():“ 此功能首先在Python 3.0中删除,然后在Python 3.2中恢复。
塔德克

在python 3.8中似乎仅tp_call检查的存在。请参阅PyCallable_Check的实现,这是3行。
Michele Piccolini

84

从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;
    }
}

它说:

  1. 如果对象是一些类的实例,那么它是可调用当且仅当它有__call__属性。
  2. 其他对象x是可调用的iff 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')

10
我发现该调用方法的示例极具误导性,因为它与用于缓存和装饰器的配方混合在一起,这对调用
FlorianBösch08年

3
JF Sebastian,还堆积了很多您从其他地方复制粘贴的示例,这些示例并非最小。
FlorianBösch08年

20
@JF Sebastian:是BS的,更多逼真的例子更好。我可以向您展示栩栩如生的代码,让您流泪。简单的示例也可以工作,它们可以更好地说明某些事情,因为它们不会分散注意力。
FlorianBösch08年

5
您在解释什么是可调用对象,但是您举了一个示例,该示例如何使用可调用对象定义装饰器。我知道这是callable的典型用法,但是这会使想知道什么是callable以及如何使用callable的读者感到困惑。我希望使用@FlorianBösch的答案。
KFL 2012年

2
@Kay:我也喜欢@FlorianBösch的回答(以当前形式)。顺便说一句,装饰器不是 “可调用”的典型用法。最典型的“可调用”是函数/方法,例如def f(): ...,与类作为对象,例如class C: ...f''.striplen,和C所有都调用。__call__()在类中具有方法的实例相对较少。
jfs 2012年

37

可调用对象是允许您使用圆括号()并最终传递一些参数的对象,就像函数一样。

每次定义函数时,python都会创建一个可调用对象。例如,您可以通过以下方式定义函数func(相同):

class a(object):
    def __call__(self, *args):
        print 'Hello'

func = a()

# or ... 
def func(*args):
    print 'Hello'

您可以使用此方法代替doitrun之类的方法,我认为看到obj()比obj.doit()更清楚


37

让我向后解释:

考虑一下...

foo()

...作为以下方面的语法糖:

foo.__call__()

foo响应的对象在哪里__call__?当我说任何对象时,我的意思是:内置类型,您自己的类及其实例。

对于内置类型,在编写时:

int('10')
unicode(10)

您实际上是在做:

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

这就是为什么您没有使用foo = new intPython:只需使class对象在上返回它的一个实例__call__。我认为Python解决此问题的方式非常优雅。


您本质上是在做type(int).__call__(int, '10')type(unicode).__call__(unicode, '10')。总是在类上调用Dunders,而不是通过实例调用。他们也从未经历过元类。在大多数情况下,这只是小问题,但这有时很重要。
疯狂物理学家

11

Callable是具有该__call__方法的对象。这意味着您可以伪造可调用的函数,或执行诸如Partial Function Application之类的整洁事情,在该函数中,您可以使用一个函数并添加一些可以增强其功能或填充某些参数的函数,从而返回可以依次调用的函数(在函数式编程圈中称为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
>>> 

9

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

7

很简单,“可调用”是可以像方法一样被调用的东西。内置函数“ callable()”将告诉您是否有某些东西可调用,就像检查call属性一样。函数可以像类一样被调用,类实例可以被调用。在这里这里看到更多关于这个的信息


5

在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

3

检查函数或类的方法是否可调用,这意味着我们可以调用该函数。

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

1
您确定callable(obj.__init___)没有多余的下划线(如AttributeError中所示)吗?如果不是,您确定答案不是True那个吗?
疯狂物理学家

2

您可以在其后加上“(args)”,并期望它能正常工作。可调用对象通常是方法或类。方法被调用,类被实例化。


2

可调用__call__对象实现特殊方法,因此具有这种方法的任何对象都是可调用的。


__call__如果类未定义此类方法,则您定义的实例将无法调用。
疯狂物理学家

2

可调用是带有方法调用的 “内置函数或方法”的类型或类

>>> type(callable)
<class 'builtin_function_or_method'>
>>>

示例: print是一个可调用对象。使用内置函数__call__ 调用print函数时,Python创建类型为print对象,并调用其方法__call__并传递参数(如果有)。

>>> type(print)
<class 'builtin_function_or_method'>
>>> print.__call__(10)
10
>>> print(10)
10
>>>

谢谢。问候,马里斯


1
这里的一些信息是直接错误的。例如:“调用print函数时,Python创建类型为print的对象并调用其方法__call__”。Python不会创建打印对象。它只是调用了等同于的东西type(print).__call__(print, *args, **kwargs)。第一句话没有多大意义。您似乎在混淆可调用对象和“可调用”功能。
疯狂物理学家
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.