Python 是否可以重写一个类'__调用方法?

Python 是否可以重写一个类'__调用方法?,python,python-3.x,Python,Python 3.x,我需要更改给定对象的\u调用\u方法的行为。幼稚的方法可能是这样的: class A(object): def __call__(self): return 1 def new_call(): return 42 a = A() a.__call__ = new_call 为什么它不是a()42的输出?有没有一种变通方法可以让我达到同样的效果?(不使用类) ========================================编辑=======

我需要更改给定对象的
\u调用\u
方法的行为。幼稚的方法可能是这样的:

class A(object):
    def __call__(self):
        return 1

def new_call():
    return 42


a = A()
a.__call__ = new_call
为什么它不是
a()
42的输出?有没有一种变通方法可以让我达到同样的效果?(不使用类)


========================================编辑=================================


对于记录,简短的回答是否定的。Python直接在类上而不是在实例上调用“特殊方法”,如
\uuuu call\uuu
,因此,如果需要更改方法,则需要在类本身上更改它。

因为
A.\uu call\uuu
是在
A.\uu call\uu
之前解决的。如果要为每个实例绑定
\uuuuu call\uuuu
,则必须通过尝试从此处解析来更改解析。例如

class A(object):
    def __call__(self):
        try:
            return vars(self)['__call__']()
        except KeyError:
            return 1

def new_call():
    return 42


a = A()

print(a())
a.__call__ = new_call
print(a())
请注意:

1
42
如果您希望它成为一种方法,并且可以访问
self
,那么您必须绑定它。例如

from types import MethodType

class A(object):
    def __call__(self):
        try:
            return vars(self)['__call__']()
        except KeyError:
            return 1

def new_call_method(self):
    return self

a = A()
a.__call__ = MethodType(new_call_method, a)
also_a = a()

将返回
a

通常,通过对象的类型而不是实例的属性集合来访问特殊的方法名称,例如
\uuuuuuu调用
。发件人:

如果一个类定义了一个名为
\uuuu getitem\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

这也适用于调用
a()
大致相当于
类型(a)。\uuu调用\uuuu(a)
。更改
a
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
属性没有效果,因为代码正在查找a的
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

将一个新函数分配给
a._\u调用_
应该可以执行您想要的操作

class A(object):
    def __call__(self):
        return 1

def new_call(self):
    return 42


a = A()
A.__call__ = new_call
print(a())
结果:

42
特殊方法(也称为“dunder”方法)是针对对象的类进行查找的,因此要覆盖它,您需要更改类,而不是实例。还要注意,所有方法都有一个initail参数传递给它们,通常称为
self

以下操作可以满足您的要求(但请注意,这将影响该类的所有实例):

如果您只想更改一个特定实例,一个相对简单的解决方法是使类“
\uu call\”
方法调用一个不像它那样“特殊”的方法,即引入一个间接级别

我的意思是:

# Workaround

class B(object):
    def __call__(self):
        return self.call_method(self)

    @staticmethod
    def call_method(self):
        return 1

def new_call(self):
    return 42

# Create two instances for testing.
b1 = B()
b2 = B()
b2.call_method = new_call  # Will only affect this instance.

print(b1())  # -> 1
print(b2())  # -> 42

首先,你不需要在新的通话中加入自我论证吗?为什么不将A类子类化呢?这会改变
A
的所有实例的
调用
,而不仅仅是
A
,我认为这不是OP想要的。确切地说,这种方式我不能改变两个不同的对象是另一种方式。尽管如此,还是要感谢你的宝贵解释嗯,很棘手。在AICT中,即使类没有定义方法,特殊方法查找也不需要查看实例的属性。因此很难获得每个实例的行为。Jordan Brière的方法,或者类似的方法,可能是将
\u call\u
的行为显式“分派”到每个实例的最简单方法。第二次尝试失败,因为魔术方法直接指向类属性,而不是实例属性。第二次尝试依赖于第一次
定义。我没有重新定义它,以避免重复代码。不,关键是
a(…)
总是
a.\uu调用
,无论
a.\uu调用
是否定义。这就是为什么
a.\uu调用
首先尝试
返回vars(self)[“uu调用”],它将从实例
a.\uu call\uu
解析,或者回退到
return 1
@jordanbrie.\uu这里有两个完全不同的解决方案:第一个解决方案有效,因为您将
a.\uu call\uu
进行了推广。第二个没有,因为您不能通过定义
A.\uu调用来“抢占”
A.\uu调用。这根本不是
()
调用语法的工作方式。
# Workaround

class B(object):
    def __call__(self):
        return self.call_method(self)

    @staticmethod
    def call_method(self):
        return 1

def new_call(self):
    return 42

# Create two instances for testing.
b1 = B()
b2 = B()
b2.call_method = new_call  # Will only affect this instance.

print(b1())  # -> 1
print(b2())  # -> 42