删除现有的类变量yield AttributeError [英] Deleting existing class variable yield AttributeError
问题描述
我正在通过Python的元类操纵类的创建.但是,尽管一个类由于其父类而具有属性,但我无法将其删除.
I am manipulating the creation of classes via Python's metaclasses. However, although a class has a attribute thanks to its parent, I can not delete it.
class Meta(type):
def __init__(cls, name, bases, dct):
super().__init__(name, bases, dct)
if hasattr(cls, "x"):
print(cls.__name__, "has x, deleting")
delattr(cls, "x")
else:
print(cls.__name__, "has no x, creating")
cls.x = 13
class A(metaclass=Meta):
pass
class B(A):
pass
上面的代码的执行在创建类B
时产生一个AttributeError
:
The execution of the above code yield an AttributeError
when class B
is created:
A has no x, creating
B has x, deleting
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-3-49e93612dcb8> in <module>()
10 class A(metaclass=Meta):
11 pass
---> 12 class B(A):
13 pass
14 class C(B):
<ipython-input-3-49e93612dcb8> in __init__(cls, name, bases, dct)
4 if hasattr(cls, "x"):
5 print(cls.__name__, "has x, deleting")
----> 6 delattr(cls, "x")
7 else:
8 print(cls.__name__, "has no x, creating")
AttributeError: x
为什么不能删除现有属性?
Why can't I delete the existing attribute?
编辑:我认为我的问题与类实例上的delattr产生意外的AttributeError 不同它试图通过实例删除一个类变量.相反,我尝试通过类(别名实例)删除类变量(别名实例).因此,在这种情况下,给定的修复程序不起作用.
I think my question is different to delattr on class instance produces unexpected AttributeError which tries to delete a class variable via the instance. In contrast, I try to delete a class variable (alias instance) via the class (alias instance). Thus, the given fix does NOT work in this case.
EDIT2 :olinox14是正确的,这是删除父类的属性"的问题.问题可以减少为:
olinox14 is right, it's an issue of "delete attribute of parent class". The problem can be reduced to:
class A:
x = 13
class B(A):
pass
del B.x
推荐答案
在简化版本中得出的结论很简单:属性"x"不在类中,而是在超类中,并且是正常的Python属性查找将从那里读取以进行读取-编写时,即设置一个新的cls.x
会在其子类中创建一个本地x:
As you concluded in your simplified version, what goes on is simple: the attribute "x" is not in the class, it is in the superclasses, and normal Python attribute lookup will fetch it from there for reading - and on writting, that is, setting a new cls.x
will create a local x in he subclass:
In [310]: class B(A):
...: pass
...:
In [311]: B.x
Out[311]: 1
In [312]: del B.x
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-312-13d95ac593bf> in <module>
----> 1 del B.x
AttributeError: x
In [313]: B.x = 2
In [314]: B.__dict__["x"]
Out[314]: 2
In [315]: B.x
Out[315]: 2
In [316]: del B.x
In [317]: B.x
Out[317]: 1
如果需要在继承的类中禁止属性,则可以通过元类中的自定义__getattribute__
方法(而不是__getattr__
)来实现.元类甚至不需要其他方法(尽管您可以使用它们,例如,编辑要抑制的属性列表)
If you need to suppress atributes in inherited classes, it is possible, though, through a custom __getattribute__
method (not __getattr__
) in the metaclass. There is even no need for other methods on the metaclass (though you can use them, for, for example, editing a list of attributes to suppress)
class MBase(type):
_suppress = set()
def __getattribute__(cls, attr_name):
val = super().__getattribute__(attr_name)
# Avoid some patologic re-entrancies
if attr_name.startswith("_"):
return val
if attr_name in cls._suppress:
raise AttributeError()
return val
class A(metaclass=MBase):
x = 1
class B(A):
_suppress = {"x",}
如果尝试获取B.x
,它将提高.
If one tries to get B.x
it will raise.
通过这种策略,将__delattr__
和__setattr__
方法添加到元类中,就可以删除在子类上的超类中定义的属性:
With this startegy, adding __delattr__
and __setattr__
methods to the metaclass enables one to del attributes that are defined in the superclasses just on the subclass:
class MBase(type):
_suppress = set()
def __getattribute__(cls, attr_name):
val = super().__getattribute__(attr_name)
# Avoid some patologic re-entrancies
if attr_name.startswith("_"):
return val
if attr_name in cls._suppress:
raise AttributeError()
return val
def __delattr__(cls, attr_name):
# copy metaclass _suppress list to class:
cls._suppress = set(cls._suppress)
cls._suppress.add(attr_name)
try:
super().__delattr__(attr_name)
except AttributeError:
pass
def __setattr__(cls, attr_name, value):
super().__setattr__(attr_name, value)
if not attr_name.startswith("_"):
cls._suppress -= {attr_name,}
class A(metaclass=MBase):
x = 1
class B(A):
pass
在控制台上:
In [400]: B.x
Out[400]: 1
In [401]: del B.x
In [402]: B.x
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
...
In [403]: A.x
Out[403]: 1
这篇关于删除现有的类变量yield AttributeError的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!