是否有 PyObject class 的实现允许您正确覆盖魔术方法?

Is there an implementation of the PyObject class that allows you to properly override magic methods?

所以我在一本书中读到,如果你想从 listdictstr 等内置类型进行扩展,并想覆盖魔术方法,您应该分别使用 collections 模块中的 UserListUserDictUserString。显然,这是因为基 classes 是在 CPython 中实现的,其中这些方法不会相互调用,因此覆盖它们不会产生不良影响。

我想知道是否类似于 UserList class 等等,存在一个 class 可以用来 'properly' 从 [=19 扩展=] class。我看了PyObjectdocumentation and found this, as well as this post,但我既不想在C中扩展,也不会说。我在 collections 模块或其他任何地方都找不到任何内容。

我问的原因是因为我之前的一个问题'Is it possible to transform default class dunder methods into class methods?',我有一种预感,我的方法不起作用的原因与我之前概述的相同。

如评论中所述 - 没有这样的东西,也不需要它 - object 中的所有魔术方法都可以被覆盖并且可以正常工作。

字典、列表和其他 collections 会发生什么,正如 MisterMiyagi 在评论中解释的那样,例如,dict 的 get 不会使用 __getitem__ 方法,所以如果您正在自定义此行为,您还必须重写 get。 classes 正确地解决了这个问题,允许人们用最少的可重复使用的代码创建完全可用的映射、序列和集合,它们是在 collections.abc 模块中定义的。

现在,如果您希望其中一种魔术方法适用于一个 object 的 class 而不是 class 的实例,您必须 实现 class 的 class 中的方法 - 即 "metaclass"。

这与 "superclass" 有很大不同 - superclasses 定义了 subclasses 继承的方法和属性,并且将在 subclass 中可用]是的。但是 class 上的魔术方法只影响实例,而不影响 class 本身(__init_subclass__ 除外,当然 __new__ 可以更改为除了创建新实例之外的其他事情)。

Metaclasses 控制 classes 的构建方式(使用 __new____init____call__ 方法)- 并允许通过魔术方法获得有关它们 行为方式 的方法 - 我认为元 magic-methods 中的方式没有特殊情况class 在 classes 中工作,与它们在 class 与普通实例的关系中工作相比。如果您在 metaclass 上实现 __add____getitem____len__,所有这些都将适用于使用该 meta[=52= 创建的 classes ].

如果您不想在 metaclass 本身中为 class 编写魔术方法,可以做的是创建一个 metaclass将在被调用时自动创建另一个动态元 class,并将 dunder 方法复制到那个 class。但是很难将其视为任何严肃应用程序中的 "healthy" 设计 - 将魔术方法应用于 classes 已经有点过头了 - 尽管在某些情况下这很方便.

因此,例如,如果您希望 class 有一个 __geitem__,这将允许您检索 class 的所有实例,这将有效:

class InstanceRegister(type):
    def __init__(cls, name, bases, namespace, **kw):
        super().__init__(name, bases, namespace, **kw)

        cls._instances = []

        original_new = cls.__new__
        def new_wrapper(cls, *args, **kw):
            if original_new is object.__new__:
                instance = original_new(cls)
            else:
                instance = original_new(cls, *args, **kw)
            cls._instances.append(instance)
            return instance

        cls.__new__ = new_wrapper

    def __len__(cls):
        return len(cls._instances)

    def __getitem__(cls, item):
        return cls._instances[item]

这会起作用,正如在这个互动中所显示的那样 session:

In [26]: class A(metaclass=InstanceRegister): pass                                                                                   

In [27]: a = A()                                                                                                                     

In [28]: len(A)                                                                                                                      
Out[28]: 1

In [29]: A[0] is a                                                                                                                   
Out[29]: True