难以理解为什么我能够在类之外重新定义(猴子补丁)__eq__
,但不能通过__init__
或方法更改其定义:
class SpecialInteger:
def __init__(self,x):
self.x = x
self.__eq__ = self.equals_normal
def equals_normal(self,other):
return self.x == other.x
def equals_special(self,other):
return self.x != other.x
def switch_to_normal(self):
self.__eq__ = self.equals_normal
def switch_to_special(self):
self.__eq__ = self.equals_special
a = SpecialInteger(3)
b = SpecialInteger(3)
print(a == b) # false
a.switch_to_normal()
print(a == b) # false
SpecialInteger.__eq__ = SpecialInteger.equals_normal
print(a == b) # true
SpecialInteger.__eq__ = SpecialInteger.equals_special
print(a == b) # false
我只是错误地使用self
还是有其他原因它是这样的?
要在课堂上进行,您只需在类中定义__eq__
方法。
class SpecialInteger:
def __init__(self,x):
self.x = x
def __eq__(self, other):
# do stuff, call whatever other methods you want
编辑:我看到你在问什么,你希望在实例级别覆盖该方法(这是一种“魔法”方法)。我不相信这在语言的基础结构中是可能的,per this discussion。
您的猴子补丁在该示例中工作的原因是因为它在类级别上传递,而不是实例级别,而self
是指实例。
保持相同功能的最简单方法是仅引用__eq__
中的其他变量。它可以是一些参考变量,也可以是保存的方法。
class SpecialInteger:
def __init__(self,x):
self.x = x
self._equal_method = self.equals_normal
# ...
def switch_to_normal(self):
self._equal_method = self.equals_normal
def switch_to_special(self):
self._equal_method = self.equals_special
def __eq__(self, other):
return self._equal_method(other)
只是添加一个优秀的现有答案,但这不起作用,因为您正在修改类实例,而不是类。
为了获得你想要的行为,你可以在__init__
期间修改类,但是,这是非常不合适的(因为它修改了类,因此修改了类的所有实例),你最好在班级范围。
例如,以下内容是等效的:
class SpecialInteger1:
def __init__(self,x):
self.x = x
self.__class__.__eq__ = self.equals_normal
...
class SpecialInteger2:
def __init__(self,x):
self.x = x
def equals_normal(self,other):
return self.x == other.x
def __eq__(self, other):
return self.equals_normal(other)
在所有示例中,您应该首选案例SpecialInteger2
,因为它更清楚它的作用。
但是,这些都不能解决您要解决的问题:如何在实例级别创建可以切换的专门的相等比较?答案是通过使用枚举(在Python 3中):
from enum import Enum
class Equality(Enum):
NORMAL = 1
SPECIAL = 2
class SpecialInteger:
def __init__(self, x, eq = Equality.NORMAL):
self.x = x
self.eq = eq
def equals_normal(self, other):
return self.x == other.x
def equals_special(self, other):
return self.x != other.x
def __eq__(self, other):
return self.__comp[self.eq](self, other)
# Define a dictionary for O(1) access
# to call the right method.
__comp = {
Equality.NORMAL: equals_normal,
Equality.SPECIAL: equals_special
}
让我们快速介绍一下,因为有三个部分:
eq
的实例成员变量,可以动态修改。__eq__
的一个实现,它根据self.eq
的值选择正确的相等函数。__
开头的类/成员变量,在本例中为self.__comp
),它允许有效地查找所需的相等方法。字典可以轻松完成,特别是对于您只希望支持1-5种不同的可能比较的情况,并用惯用的if/then
语句替换,但是,如果您希望支持更多的比较选项(例如,300) ,字典将比if / then比较(线性搜索,O(1)
)更有效O(n)
。
如果您希望使用setter(如原始示例中)执行此操作,并实际隐藏用户的成员函数,您也可以通过直接将函数存储为变量来完成此操作。
所有方法定义都在类级别定义(字面上,名称是属于该类的dict中的键)。你在课堂上放的其他东西也是如此。这就是为什么例如类中方法之外的变量赋值会产生类变量的原因。