[英]Class attribute which is not present on subclasses
主要是為了好玩,為了幫助我正在編寫的解析器中的一些人體工程學,我非常想覆蓋一個類' __getattr__
而不對其子類做同樣的事情。 為了解釋,一些代碼:
class AttrMeta(type):
def __getattr__(self, name):
if name == "A":
return "hi there."
class Main(metaclass=AttrMeta):
pass
class Subclass(Main):
pass
print(Main.A) # => hi there.
print(Subclass.A) # => hi there.
print(Main().A) # => AttributeError: 'Main' object has no attribute 'A'
print(Subclass().A) # => AttributeError: 'Subclass' object has no attribute 'A'
這很正常,但我希望Subclass
不繼承__getattr__
。 為此,我嘗試的第一件事是在AttrMeta
中覆蓋__new__
,如下所示:
# in AttrMeta...
def __new__(mcs, name, bases, namespace, **kwargs):
if bases:
return type.__new__(type, name, bases, namespace, **kwargs)
return type.__new__(mcs, name, bases, namespace, **kwargs)
這里的想法是從Main
的子類中剝離元類,這些子類是通過查看bases
來檢測的。 在創建class Subclass(Main)
時,這導致了一些無限遞歸,這是有道理的——你怎么會有這樣一個損壞的 inheritance 鏈?
我的下一個想法是手動刪除__getattr__
:
# in AttrMeta...
def __new__(mcs, name, bases, namespace, **kwargs):
new_cls = type.__new__(mcs, name, bases, namespace, **kwargs)
if bases:
del new_cls.__getattr__
return new_cls
好吧,回想起來很明顯你不能這樣做:你得到一個AttributeError: __getattr__
。 那是因為Subclass.__getattr__
綁定到AttrMeta
- 事實上, "__getattr__" not in dir(Subclass)
成立。
這時,我對自己說,嘿。 Tim Peters 說需要元類的人都知道這一點,而我根本不是那些人中的一員。 事實上,我根本“不需要”編寫這個解析器,我只是想,你知道,在隔離期間得到一點消遣。 也許我應該通過分配給__getattr__
上的 __getattr__ 來做到這一點,然后看看會發生什么。 所以,我嘗試了:
class Main:
pass
class Subclass(Main):
pass
def __getattr__(self, name):
if name == "A":
return "hi there."
Main.__getattr__ = __getattr__
Main.A # raises AttributeError: type object 'Main' has no attribute 'A'
在這一點上,我已經沒有想法了。 我可能會放棄並允許Subclass
擁有屬性 getter。 我不想在每個Subclass
上覆蓋__getattr__
因為有很多子類,但我很好奇是否有任何想法。 感謝您閱讀這篇長文。
啊,我想出了至少一種可行的方法。 這個想法是像這樣添加一個新的元類:
class AttrMeta(type):
def __new__(mcs, name, bases, namespace, **kwargs):
if bases:
mcs = NoMoreMeta
return type.__new__(mcs, name, bases, namespace, **kwargs)
def __getattr__(self, name):
if name == "A":
return "hi there."
class NoMoreMeta(AttrMeta):
def __getattr__(self, name):
if name == "A":
raise AttributeError(name)
class Main(metaclass=AttrMeta):
pass
class Subclass(Main):
pass
然后Main.A
像上面一樣工作,但Subclass.A
會引發相應的錯誤。
不能在 Python 中將屬性或方法設置為“不繼承” - 您可以僅在子類上隱藏先前存在的方法或屬性,並且可以使用它來注入會引發 AttributeError 的替換。
在這種情況下,由於 Python 3.6,您甚至不需要元類 - 您可以使用__init_subclass__
方法,該方法在創建子類(但不是當前類)時運行,將__getattr__
替換為會引發:
class Main:
def __getattr__(self, attr):
if attr == "A":
return "A"
raise AttributeError()
def __init_subclass__(cls, *args, **kw):
super().__init_subclass__(*args, **kw)
def __getattr__(self, attr):
# blocks the original getattr
raise AttributeError(attr)
# optional check: allow subclasses to implement their own __getattr__ methods:
if "__getattr__" in cls.__dict__:
return
cls.__getattr__ = __getattr__
class SubClass(Main):
pass
這運行:
In [95]: Main().A
Out[95]: 'A'
In [96]: SubClass().A
---------------------------------------------------------------------------
AttributeError
但是,您正在將__getattr__
添加到元類本身 - (我感到羞恥,我現在才看到它) - 所以動態屬性可以出現在類上,而不僅僅是實例上。
go 它們的更簡單方法是使用某些屬性標記原始“基礎” class,並在元類__getattr__
上檢查是否在該基礎 ZA2F2ED4F8EBC2CBB4C21A29DZ40AB6 上調用 getattr
( __init_subclass__
方法不起作用,因為元類本身是相同的,主 class 子類沒有使用“元類的子類”)
mark = "_AttrBase"
def find_marked(cls):
for sclass in cls.__mro__:
if sclass.__dict__.get(mark, False):
return sclass
return None
class AttrMeta(type):
def __new__(mcls, name, bases, namespace, **kwd):
cls = super().__new__(mcls, name, bases, namespace, **kwd)
if not find_marked(cls):
# This line injects a getattr on the instances as well, so
# that instances have the same behavior for these as the classes:
cls.__getattr__ = lambda self, attr: getattr(self.__class__, attr)
setattr(cls, mark, True)
return cls
def __getattr__(cls, attr):
if find_marked(cls) is not cls:
raise AttributeError(attr)
if attr == "A":
return "A"
raise AttributeError(attr)
class Main(metaclass=AttrMeta):
pass
class SubClass(Main):
pass
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.