Как обеспечить дополнительную инициализацию для подкласса 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, особенно с кортежами. Есть ли подводные камни для этого решения? Есть ли способ, как это сделать? Это нормально? Спасибо заранее.
Ответы
Ответ 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.