1 回答

TA貢獻1795條經驗 獲得超7個贊
正如評論中所述 - 沒有這樣的東西,也不需要它 - 中的所有魔術方法都object可以被覆蓋并且可以正常工作。
字典、列表和其他集合會發生什么,正如 MisterMiyagi 在評論中解釋的那樣,例如,dictget不會使用該__getitem__方法,因此如果您要自定義其行為,您還必須重寫get. 正確解決這個問題的類,允許人們用最少的可重用代碼創建完全可用的映射、序列和集合,這些類是在collections.abc模塊中定義的。
現在,如果您希望其中一種魔術方法對一個對象的類而不是該類的實例起作用,則必須在該類的類中實現這些方法——這就是“元類”。
這與“超類”有很大不同——超類定義了子類繼承的方法和屬性,這些方法和屬性在子類中可用。但是類上的魔術方法只影響實例,而不影響類本身(__init_subclass__, 和當然除外__new__,它可以更改為做其他事情而不是創建新實例)。
元類控制類的構建方式 (使用__new__,__init__和__call__方法) - 并且允許擁有關于它們如何通過魔術方法表現的方法 - 我認為元類中的魔術方法如何在類中工作沒有特殊情況,與它們在類與普通實例的關系中的工作相比。如果您在元類上實現__add__, __getitem__,__len__所有這些都將適用于使用該元類創建的類。
如果您不想在元類本身中為該類編寫魔術方法,可以做的是創建一個元類,該元類將在被調用時自動創建另一個動態元類,并復制 dunder 方法到那個班級。但很難將其視為任何嚴肅應用程序中的“健康”設計——將魔法方法應用于類已經有點過頭了——盡管在某些情況下這樣做很方便。
因此,例如,如果您希望一個類具有一個__geitem__允許您檢索該類的所有實例的類,這將起作用:
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]
這將起作用,正如在本次互動會議中所顯示的那樣:
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
添加回答
舉報