删除现有的类变量yield AttributeError [英] Deleting existing class variable yield AttributeError

查看:74
本文介绍了删除现有的类变量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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆