自动使类可以清洗

时间:2012-09-20 12:24:30

标签: python attributes python-3.x hashable

有几种标准方法可以使类可以使用,例如(从SO借用):

# assume X has 2 attributes: attr_a and attr_b
class X:
  def __key(self):
    return (self.attr_a, self.attr_b)

  def __eq__(x, y):
    return isinstance(y, x.__class__) and x.__key() == y.__key()

  def __hash__(self):
    return hash(self.__key())

现在假设我有许多我想要制作的课程。它们都是不可变的,具有不可变属性,散列所有这些属性是可以接受的(对于具有太多属性的类,我们只想散列一些足以避免大多数冲突的属性)。我可以避免为每个班级手工编写__key()方法吗?

创建一个为其定义__key()__eq____hash__的基类是不是一个好主意?特别是,我不确定是否可以找到应该进入__hash__的所有实例属性。我知道这是generally impossible,但在这种情况下,我们可以假设更多关于对象(例如,它是不可变的 - 在__init__完成后,它的属性都是可清除的等等。)

(如果继承层次结构不起作用,也许装饰者会这么做?)

2 个答案:

答案 0 :(得分:4)

实例将其属性存储在self.__dict__

>>> class Foo(object):
...     def __init__(self, foo='bar', spam='eggs'):
...         self.foo = foo
...         self.spam = spam
... 
>>> f = Foo()
>>> f.__dict__
{'foo': 'bar', 'spam': 'eggs'}

如果您没有在实例上存储任何方法,则默认.__key()可以是:

def __key(self):
    return tuple(v for k, v in sorted(self.__dict__.items()))

我们按属性名称对项目进行排序; tuple()调用确保我们返回适合hash()调用的不可变序列。

对于更复杂的设置,您必须测试values()返回的类型(跳过函数等)或使用特定的属性模式或重新调整__slots__列出适当的属性可以使用。

与您的__hash____eq__方法一起构建一个良好的基类来继承所有不可变类。

答案 1 :(得分:1)

如果您假设属性的约定,则可以执行此操作。在你的例子中, 这将是非常微不足道的,因为你的属性以“attr_”开头 - 所以你可以将你的__key方法编写为:

def __key(self):
    return tuple (getattr(self, attr) for attr in self.__dict__ if attr.startswith("attr_") )

正如您所看到的 - 您可以找到的任何测试都可以满足您的需求。

我可以给你的建议是让你的课程使用Python的__slots__功能: 这样不仅可以轻松查找属性名称,还可以使您的可变对象使用效率更高,内存占用更少。

class X:
    __slots__ = ("a", "b", "c")
    def __key(self):
        return tuple (getattr(self, attr) for attr in self.__class__.__slots__ )

修改 回答O.P。的第一条评论:

当然,这适用于继承。如果你总是使用它们的所有对象的属性,你不需要表达式的“if”部分 - 将函数写为_key(而不是__key,它为一个唯一的名称每个类在内部)在层次结构顶部的类上,它将适用于所有类。