声明子函数__call__的子方法在定义后无法调用

时间:2017-10-07 20:56:32

标签: python

>>> def f():
...        def __call__(x):
...             return x
... 
>>> f(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() takes 0 positional arguments but 1 was given
>>> f.__call__(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() takes 0 positional arguments but 1 was given

问题是在定义f及其私有子功能__call__时 - 以及在尝试调用该函数之后(认为它会调用__call__覆盖)它会抛出一个错误,指出函数没有接受任何参数,但是传递了一个。

而且(很明显 - 后见之明)试图调用__call__方法,它会给出完全相同的错误。

关于这一点,我很困惑,因为我认为首先会定义f,然后它会创建基本的私有方法(如__str____name__,{ {1}}等等,然后显式__repr__方法的定义将取代隐式生成的__call__方法。

有人可以解释函数定义的顺序是什么,以及首先和最后声明的内容,或解释如何设法覆盖隐藏的__call__(如果可能的话,最好不使用类来掩盖一个功能)。

2 个答案:

答案 0 :(得分:4)

函数中的命名空间不会生成属性。 __call__f()内的局部变量,仅此而已。

def <identifier>(...):绑定当前命名空间中的名称identifier,并在函数中绑定一个本地名称,就像some_name = ...一样。

最多可以返回该对象:

>>> def f():
...     def __call__(x):
...         return x
...     return __call__  # return the local variable
...
>>> f()
<function f.<locals>.__call__ at 0x10e946400>
>>> f()(1)
1

如果函数中的所有局部变量都成为属性,甚至在您实际执行该函数之前,这将是非常令人惊讶的!

但是,即使您为函数对象提供了__call__属性,仍然不会使用。您只能使用创建一个__call__方法的对象:

class Foo:
    def __call__(self, x):
        return x

foo = Foo()
foo()  # returns 1

那是因为__call__是一个特殊方法,因此受Special method lookup rules的约束,后者规定Python会在类型上寻找方法对象,而不是对象本身。

当Python评估foo(...)时,如果foo不是函数对象,Python将执行type(foo).__call__(foo, ...)。 Python这样做是为了确保在使用Foo.__call__创建实例时不使用Foo()方法;类也是可调用的,但定义Foo.__call__不应该与之冲突。

答案 1 :(得分:0)

您要求避免的是__call__的用途。它允许带有该方法的对象伪装成一个函数。当您定义一个函数时,您不会创建一个具有属性的对象 - 根本没有定义名为__call__的方法(至少您自己定义了...)表示函数f的对象。

然而,事实证明你已经有一个处理你的电话的功能,即f。如果需要,您也可以使用装饰器来包装该功能,或者如您所建议的那样,创建一个类并改为实例化对象。