如何将特殊方法“ Mixin”应用于键入。

Dam*_*ria 5 python namedtuple python-3.6

我喜欢typing.NamedTuplePython 3.6中的。但是通常情况下,namedtuple包含非哈希属性,而我想将其用作dict键或set成员。如果一个namedtuple类使用对象标识(id()for __eq____hash__)有意义,那么将这些方法添加到该类中就可以正常工作。

但是,现在我的代码在多个地方都有这个模式,我想摆脱样板__eq____hash__方法定义。我知道namedtuple'不是普通班,我还无法弄清楚如何使它正常工作。

这是我尝试过的:

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
"""
Run Code Online (Sandbox Code Playgroud)

有没有一种方法我不必在每个NamedTuple需要“对象标识”的地方重复这些方法?

geo*_*xsh 5

魔源NamedTuple类语法是它的元类 NamedTupleMeta在幕后NamedTupleMeta.__new__为您创建一个新的类,而不是一个典型,而是通过创建一个类collections.namedtuple()

问题是,当NamedTupleMeta创建新的类对象时,它忽略了基类,您可以检查的MRO TestMixinFirst,没有ObjectIdentityMixin

>>> print(TestMixinFirst.mro())
[<class '__main__.TestMixinFirst'>, <class 'tuple'>, <class 'object'>]
Run Code Online (Sandbox Code Playgroud)

您可以扩展它以处理基类:

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
Run Code Online (Sandbox Code Playgroud)

  • 仅供参考:这在 Python 3.9 中不起作用:“NamedTupleMeta.__new__”中的第一行现在有一个断言语句,表明第一个基类是“_NamedTuple” (2认同)