Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to apply a special methods 'Mixin' to a typing.NamedTuple

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?

like image 301
Damon Maria Avatar asked Nov 16 '17 21:11

Damon Maria


People also ask

What does Namedtuple on a collection type return?

NamedTuple can return the values with keys as OrderedDict type object. To make it OrderedDict, we have to use the _asdict() method.

What is an advantage of Namedtuples over dictionaries?

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.

What is one advantage of classes over named tuples?

Data classes advantages over NamedTuplemutable objects. inheritance support. property decorators, manageable attributes. generated method definitions out of the box or customizable method definitions.


1 Answers

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
like image 100
georgexsh Avatar answered Sep 20 '22 14:09

georgexsh