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