是否有 PyObject class 的实现允许您正确覆盖魔术方法?
Is there an implementation of the PyObject class that allows you to properly override magic methods?
所以我在一本书中读到,如果你想从 list
、dict
或 str
等内置类型进行扩展,并想覆盖魔术方法,您应该分别使用 collections
模块中的 UserList
、UserDict
和 UserString
。显然,这是因为基 classes 是在 CPython 中实现的,其中这些方法不会相互调用,因此覆盖它们不会产生不良影响。
我想知道是否类似于 UserList
class 等等,存在一个 class 可以用来 'properly' 从 [=19 扩展=] class。我看了PyObject
documentation 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
所以我在一本书中读到,如果你想从 list
、dict
或 str
等内置类型进行扩展,并想覆盖魔术方法,您应该分别使用 collections
模块中的 UserList
、UserDict
和 UserString
。显然,这是因为基 classes 是在 CPython 中实现的,其中这些方法不会相互调用,因此覆盖它们不会产生不良影响。
我想知道是否类似于 UserList
class 等等,存在一个 class 可以用来 'properly' 从 [=19 扩展=] class。我看了PyObject
documentation 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