I love the typing.NamedTuple
in Python 3.6. But there's often the case where the namedtuple
contains a non-hashable attribute and I want to use it as a dict
key or set
member. If it makes sense that a namedtuple
class uses object identity (id()
for __eq__
and __hash__
) then adding those methods to the class works fine.
However, I now have this pattern in my code in several places and I want to get rid of the boilerplate __eq__
and __hash__
method definitions. I know namedtuple
's are not regular classes and I haven't been able to figure out how to get this working.
Here's what I've tried:
from typing import NamedTuple
class ObjectIdentityMixin:
def __eq__(self, other):
return self is other
def __hash__(self):
return id(self)
class TestMixinFirst(ObjectIdentityMixin, NamedTuple):
a: int
print(TestMixinFirst(1) == TestMixinFirst(1)) # Prints True, so not using my __eq__
class TestMixinSecond(NamedTuple, ObjectIdentityMixin):
b: int
print(TestMixinSecond(2) == TestMixinSecond(2)) # Prints True as well
class ObjectIdentityNamedTuple(NamedTuple):
def __eq__(self, other):
return self is other
def __hash__(self):
return id(self)
class TestSuperclass(ObjectIdentityNamedTuple):
c: int
TestSuperclass(3)
"""
Traceback (most recent call last):
File "test.py", line 30, in <module>
TestSuperclass(3)
TypeError: __new__() takes 1 positional argument but 2 were given
"""
Is there a way I don't have to repeat these methods in each NamedTuple
that I need 'object identity' in?
NamedTuple can return the values with keys as OrderedDict type object. To make it OrderedDict, we have to use the _asdict() method.
Moreover, as namedtuple instances do not have per-instance dictionaries, they are lightweight and require no more memory than regular tuples. This makes them faster than dictionaries.
Data classes advantages over NamedTuplemutable objects. inheritance support. property decorators, manageable attributes. generated method definitions out of the box or customizable method definitions.
The magic source of NamedTuple
class syntax is its metaclass NamedTupleMeta
, behind the scene, NamedTupleMeta.__new__
creates a new class for you, instead of a typical one, but a class created by collections.namedtuple()
.
The problem is, when NamedTupleMeta
creating new class object, it ignored bases classes, you could check the MRO of TestMixinFirst
, there is no ObjectIdentityMixin
:
>>> print(TestMixinFirst.mro())
[<class '__main__.TestMixinFirst'>, <class 'tuple'>, <class 'object'>]
you have to extend NamedTupleMeta
to take care of base classes:
import typing
class NamedTupleMetaEx(typing.NamedTupleMeta):
def __new__(cls, typename, bases, ns):
cls_obj = super().__new__(cls, typename+'_nm_base', bases, ns)
bases = bases + (cls_obj,)
return type(typename, bases, {})
class TestMixin(ObjectIdentityMixin, metaclass=NamedTupleMetaEx):
a: int
b: int = 10
t1 = TestMixin(1, 2)
t2 = TestMixin(1, 2)
t3 = TestMixin(1)
assert hash(t1) != hash(t2)
assert not (t1 == t2)
assert t3.b == 10
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With