删除现有 class 变量 yield AttributeError
Deleting existing class variable yield AttributeError
我正在通过 Python 的元 classes 操纵 classes 的创建。但是,虽然 class 由于其父项而具有属性,但我无法删除它。
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
当创建 class B
时,上述代码的执行会产生一个 AttributeError
:
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
为什么我不能删除现有属性?
编辑: 我认为我的问题与 delattr on class instance produces unexpected AttributeError 不同,后者试图通过实例删除 class 变量。相反,我尝试通过 class (别名实例)删除 class 变量(别名实例)。因此,给定的修复在这种情况下不起作用。
EDIT2: olinox14 是对的,这是 "delete attribute of parent class" 的问题。问题可以简化为:
class A:
x = 13
class B(A):
pass
del B.x
似乎python 将x
变量注册为A class:
的参数
然后,当您尝试从 B
class 中删除它时,与 delattr
方法有一些冲突,就像在 the link 中提到的那样 @David提供鲱鱼...
解决方法 可能是明确地从 A
class 中删除参数:
delattr(A, "x")
正如您在简化版本中总结的那样,发生的事情很简单:属性 "x" 不在 class 中,它在超级 classes 中,并且正常Python 属性查找将从那里获取它以供读取 - 和写入时,即设置一个新的 cls.x
将在 subclass:
中创建一个本地 x
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
如果您需要抑制继承的 classes 中的属性,尽管可以通过 meta[=32= 中的自定义 __getattribute__
方法(不是 __getattr__
) ]. metaclass 上什至不需要其他方法(尽管您可以使用它们,例如,编辑要抑制的属性列表)
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
它会加注。
有了这个策略,将 __delattr__
和 __setattr__
方法添加到 metaclass 可以删除在 superclasses 中定义的属性子class:
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
我正在通过 Python 的元 classes 操纵 classes 的创建。但是,虽然 class 由于其父项而具有属性,但我无法删除它。
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
当创建 class B
时,上述代码的执行会产生一个 AttributeError
:
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
为什么我不能删除现有属性?
编辑: 我认为我的问题与 delattr on class instance produces unexpected AttributeError 不同,后者试图通过实例删除 class 变量。相反,我尝试通过 class (别名实例)删除 class 变量(别名实例)。因此,给定的修复在这种情况下不起作用。
EDIT2: olinox14 是对的,这是 "delete attribute of parent class" 的问题。问题可以简化为:
class A:
x = 13
class B(A):
pass
del B.x
似乎python 将x
变量注册为A class:
然后,当您尝试从 B
class 中删除它时,与 delattr
方法有一些冲突,就像在 the link 中提到的那样 @David提供鲱鱼...
解决方法 可能是明确地从 A
class 中删除参数:
delattr(A, "x")
正如您在简化版本中总结的那样,发生的事情很简单:属性 "x" 不在 class 中,它在超级 classes 中,并且正常Python 属性查找将从那里获取它以供读取 - 和写入时,即设置一个新的 cls.x
将在 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
如果您需要抑制继承的 classes 中的属性,尽管可以通过 meta[=32= 中的自定义 __getattribute__
方法(不是 __getattr__
) ]. metaclass 上什至不需要其他方法(尽管您可以使用它们,例如,编辑要抑制的属性列表)
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
它会加注。
有了这个策略,将 __delattr__
和 __setattr__
方法添加到 metaclass 可以删除在 superclasses 中定义的属性子class:
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