猴子在Python中修补__eq__

时间:2017-12-19 01:33:41

标签: python python-3.x

理解为什么我能够在类之外重新定义(猴子补丁)__eq__,但不能通过__init__或方法更改其定义: p>

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,还是有其他原因可以这样运作?

4 个答案:

答案 0 :(得分:3)

要在课堂上进行,您只需在课堂内定义__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是指实例。

答案 1 :(得分:0)

保持相同功能的最简单方法是仅引用var arr=[]; for (let i = 0;trees && i < trees .length; i++) { var item=trees[i]; var item2={ id: item.id, .... } arr.push(item2); } 中的其他变量。它可以是一些参考变量,也可以是保存的方法。

__eq__

答案 2 :(得分:0)

只是添加一个优秀的现有答案,但这不起作用,因为你正在修改类实例,而不是类。

为了获得你想要的行为,你可以在__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
    }

让我们快速介绍一下,因为有三个部分:

  1. eq的实例成员变量,可以动态修改。
  2. __eq__的实现,它根据self.eq的值选择正确的相等函数。
  3. 一个名称空间受损的字典(以__开头的类/成员变量,在本例中为self.__comp),允许有效查找所需的相等方法。
  4. 字典可以轻松完成,特别是对于您只希望支持1-5种不同的可能比较的情况,并替换为惯用的if/then语句,但是,如果您希望支持更多比较选项(比方说300),字典比if / then比较(线性搜索,O(1))效率更高O(n)

    如果您希望使用setter(如原始示例中)执行此操作,并实际隐藏用户的成员函数,您也可以通过直接将函数存储为变量来执行此操作。

答案 3 :(得分:0)

所有方法定义都是在类级别定义的(字面意思是名称是属于该类的 dict 中的键)。你在课堂上放的其他东西也是如此。这就是为什么例如类中方法之外的变量赋值会产生类变量的原因。