如何实现 __hash__ 和 __str__?

4
我有一个名为WeakBoundMethod的类(source on codereview.se)。我想知道如何实现__hash__()函数,同时Python 3自动提供了__repr__()函数,所以我不需要重新定义它,对吗?至于__str__(),我了解它是对象的可读文本表示形式;那我也需要定义它吗?有什么指导方针吗?
关于哈希函数... 我希望它基于绑定方法的__self__和__func__生成哈希值。我该如何做到这一点?

1
除非您计划将WeakBoundMethods哈希键,否则无需实现__hash__。如果您这样做,它们应该是不可变的,并且您必须同时实现__eq__ - user395760
@delnan 这更像是一个学习项目,所以实现它们并没有什么坏处。 :) - Paul Manta
2个回答

2
如果有疑问,不要实现魔法方法。默认值是有原因的,并且足够好用。在您的情况下,完全没有必要实现 __hash__ (如果您要实现它,还必须实现 __eq__ ),除非您希望有人拥有一组方法集或字典。

__str__可能会有用。在您的情况下,其结果应包括:

  • 类名,以避免与其他任何内容混淆
  • 函数是否存活
  • 如果它存活,则使用str()结果来识别该函数,例如名称

这不是一个真实的项目。我正在学习如何使用Python,并且我想学习如何编写哈希函数。 :) - Paul Manta
1
@Paul,这个类不应该有__hash__函数。通常,最好的实现方式是对类的某些表示进行哈希,例如hash((self.a, self.b)) - phihag

0

虽然我来晚了9年,但是因为这个问题在我的搜索结果中排名第一...

关于:

此外,Python 3自动提供了一个repr()函数,所以我想我不需要重新定义它了(?)。那么str()呢?我知道它是对象的可读文本表示形式;我应该也定义它吗?有什么指导方针吗?

默认实现很好,直到它们不再好用(即,直到你需要更有用的东西)。

举个例子:

class Message:
    _msg: str

    def __init__(self, msg: str):
        self._msg = msg

    # ... other custom behavior that makes this more useful than just a string


print(Message("asdf"))       # '<__main__.Wat object at 0x104a99128>'
print(str(Message("asdf")))  # '<__main__.Wat object at 0x104a991d0>'
print(repr(Message("asdf"))) # '<__main__.Wat object at 0x104a991d0>'

当然,缺省值是存在的,但是如果你有一些有用的信息需要打印到屏幕上、日志中等,那么使用<module.class object at address>格式真的很有帮助吗?

与此相反...

class Message:
    _msg: str
    _urgent: bool

    def __init__(self, msg: str, urgent:bool = False):
        self._msg = msg
        self._urgent = urgent

    @classmethod
    def urgent(cls, msg: str) -> "Message":
        return cls(msg, urgent=True)

    def __str__(self) -> str:
        if not self._urgent:
            return self._msg

        return f"URGENT: {self._msg.upper()}"

    def __repr__(self) -> str:
        return f"{type(self).__name__}({self})"

    # ... other custom behavior that makes this more useful than just a string


print(Message("hey there!"))                   # hey there!
print(str(Message("hey there!")))              # hey there!
print(str(Message.urgent("look behind you")))  # URGENT: LOOK BEHIND YOU
print(repr(Message.urgent("look behind you"))) # Message(URGENT: LOOK BEHIND YOU)

上面的建议,“如果不确定,就不要实现魔术方法。”部分正确(如果您不需要功能,为什么要实现它?),但它似乎也表明您可能不应该这样做。魔术方法实际上是用来被覆盖的 - 这是我们如何使用 Python 中“内置”的有用行为丰富我们的类,并避免许多其他必要的样板文件来序列化字符串、创建自定义迭代器或上下文管理器等。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接