Подтвердить что ты не робот

Как обеспечить дополнительную инициализацию для подкласса namedtuple?

Предположим, что у меня есть namedtuple:

EdgeBase = namedtuple("EdgeBase", "left, right")

Я хочу реализовать пользовательскую хеш-функцию для этого, поэтому создаю следующий подкласс:

class Edge(EdgeBase):
    def __hash__(self):
        return hash(self.left) * hash(self.right)

Поскольку объект является неизменным, я хочу, чтобы значение хеша рассчитывалось только один раз, поэтому я делаю это:

class Edge(EdgeBase):
    def __init__(self, left, right):
        self._hash = hash(self.left) * hash(self.right)

    def __hash__(self):
        return self._hash

Это, кажется, работает, но я действительно не уверен в подклассе и инициализации в Python, особенно с кортежами. Есть ли подводные камни для этого решения? Есть ли способ, как это сделать? Это нормально? Спасибо заранее.

4b9b3361

Ответ 1

edit для 2017: получается namedtuple - отличная идея. attrs - это современная альтернатива.

class Edge(EdgeBase):
    def __new__(cls, left, right):
        self = super(Edge, cls).__new__(cls, left, right)
        self._hash = hash(self.left) * hash(self.right)
        return self

    def __hash__(self):
        return self._hash

__new__ - это то, что вы хотите назвать здесь, потому что кортежи неизменяемы. Неизменяемые объекты создаются в __new__, а затем возвращаются пользователю, а не заполняются данными в __init__.

cls должен быть передан дважды вызову super на __new__, потому что __new__ для неявных/нечетных причин неявно <<29 > .

Ответ 2

Код в вопросе может выиграть от супервызов в __init__, если он когда-нибудь получит подклассу в ситуации множественного наследования, но в остальном это правильно.

class Edge(EdgeBase):
    def __init__(self, left, right):
        super(Edge, self).__init__(a, b)
        self._hash = hash(self.left) * hash(self.right)

    def __hash__(self):
        return self._hash

В то время как кортежи только для чтения, только части кортежей их подклассов только для чтения, другие свойства могут быть записаны как обычно, что позволяет присваивать _hash независимо от того, было ли это сделано в __init__ или __new__. Вы можете сделать подкласс полностью readonly, установив его __slots__ в(), который имеет дополнительное преимущество сохранения памяти, но тогда вы не сможете назначить _hash.