Python:删除子类中的类属性

发布于 2024-11-08 21:20:14 字数 378 浏览 0 评论 0 原文

我有一个子类,我希望它包含基类上存在的类属性。

我尝试了这个,但它不起作用:

>>> class A(object):
...     x = 5
>>> class B(A):
...     del x
Traceback (most recent call last):
  File "<pyshell#1>", line 1, in <module>
    class B(A):
  File "<pyshell#1>", line 2, in B
    del x
NameError: name 'x' is not defined

我该怎么做?

I have a subclass and I want it to not include a class attribute that's present on the base class.

I tried this, but it doesn't work:

>>> class A(object):
...     x = 5
>>> class B(A):
...     del x
Traceback (most recent call last):
  File "<pyshell#1>", line 1, in <module>
    class B(A):
  File "<pyshell#1>", line 2, in B
    del x
NameError: name 'x' is not defined

How can I do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

禾厶谷欠 2024-11-15 21:20:14

您可以使用 delattr(class, field_name) 将其从类定义中删除。

You can use delattr(class, field_name) to remove it from the class definition.

小梨窩很甜 2024-11-15 21:20:14

你不需要删除它。只需覆盖它即可。

class B(A):
   x = None

或者干脆不引用它。

或者考虑不同的设计(实例属性?)。

You don't need to delete it. Just override it.

class B(A):
   x = None

or simply don't reference it.

Or consider a different design (instance attribute?).

情丝乱 2024-11-15 21:20:14

没有一个答案对我有用。

例如,delattr(SubClass, "attrname")(或其完全相同的,del SubClass.attrname)不会“隐藏”父方法,因为这不是隐藏方法方法解析工作。它会失败,并显示 AttributeError('attrname',),因为子类没有 attrname。当然,用 None 替换属性并不会真正删除它。

让我们考虑这个基类:

class Spam(object):
    # Also try with `expect = True` and with a `@property` decorator
    def expect(self):
        return "This is pretty much expected"

我只知道两种对其进行子类化的方法,即隐藏 expect 属性:

  1. 使用 __get__ 引发 AttributeError。在属性查找时,会出现异常,通常与查找失败无法区分。

    最简单的方法就是声明一个引发AttributeError的属性。这基本上就是 @JBernardo 的建议。

    类西班牙宗教裁判所(垃圾邮件):
        @财产
        def 期望(自我):
            raise AttributeError("没有人预料到西班牙宗教裁判所!")
    
    断言 hasattr(Spam, "expect") == True
    # 断言 hasattr(SpanishInquisition, "expect") == False # 失败!
    断言 hasattr(SpanishInquisition(), "expect") == False
    

    但是,这仅适用于实例,不适用于类(hasattr(SpanishInquisition, "expect") == True 断言将被破坏)。

    如果您希望上述所有断言成立,请使用以下内容:

    类 AttributeHider(对象):
        def __get__(自身、实例、所有者):
            raise AttributeError("这不是您要查找的属性")
    
    西班牙宗教裁判所类(垃圾邮件):
        期望 = AttributeHider()
    
    断言 hasattr(Spam, "expect") == True
    断言 hasattr(SpanishInquisition, "expect") == False # 有效!
    断言 hasattr(SpanishInquisition(), "expect") == False
    

    我相信这是最优雅的方法,因为代码清晰、通用且紧凑。当然,如果删除属性是他们真正想要的,那么应该真的三思而后行。

  2. 使用 __getattribute__ 魔术方法覆盖属性查找 。您可以在子类(或 mixin,如下面的示例中,因为我只想编写一次)中执行此操作,这将隐藏子类实例上的属性。如果您还想对子类隐藏该方法,则需要使用元类。

    class ExpectMethodHider(对象):
        def __getattribute__(自身,名称):
            如果名称==“期望”:
                raise AttributeError("没有人预料到西班牙宗教裁判所!")
            返回 super().__getattribute__(名称)
    
    类 ExpectMethodHidingMetaclass(ExpectMethodHider, 类型):
        经过
    
    # 我在这里使用了 Python 3.x,因此语法也是如此。
    # 对于 Python 2.x 使用 __metaclass__ = ExpectMethodHidingMetaclass
    西班牙宗教裁判所类(ExpectMethodHider,垃圾邮件,
                             元类=ExpectMethodHidingMetaclass):
        经过
    
    断言 hasattr(Spam, "expect") == True
    断言 hasattr(SpanishInquisition, "expect") == False
    断言 hasattr(SpanishInquisition(), "expect") == False
    

    这看起来比上面的方法更糟糕(更冗长且不太通用),但人们也可以考虑这种方法。

    请注意,这不适用于特殊(“魔术”)方法(例如__len__),因为它们会绕过__getproperty__。查看 Python 文档的特殊方法查找部分更多细节。如果这是您需要撤消的,只需覆盖它并调用 object 的实现,跳过父级。

不用说,这只适用于“新式类”(从 object 继承的类),因为那里不支持魔术方法和描述符协议。希望这些都成为过去。

None of the answers had worked for me.

For example delattr(SubClass, "attrname") (or its exact equivalent, del SubClass.attrname) won't "hide" a parent method, because this is not how method resolution work. It would fail with AttributeError('attrname',) instead, as the subclass doesn't have attrname. And, of course, replacing attribute with None doesn't actually remove it.

Let's consider this base class:

class Spam(object):
    # Also try with `expect = True` and with a `@property` decorator
    def expect(self):
        return "This is pretty much expected"

I know only two only ways to subclass it, hiding the expect attribute:

  1. Using a descriptor class that raises AttributeError from __get__. On attribute lookup, there will be an exception, generally indistinguishable from a lookup failure.

    The simplest way is just declaring a property that raises AttributeError. This is essentially what @JBernardo had suggested.

    class SpanishInquisition(Spam):
        @property
        def expect(self):
            raise AttributeError("Nobody expects the Spanish Inquisition!")
    
    assert hasattr(Spam, "expect") == True
    # assert hasattr(SpanishInquisition, "expect") == False  # Fails!
    assert hasattr(SpanishInquisition(), "expect") == False
    

    However, this only works for instances, and not for the classes (the hasattr(SpanishInquisition, "expect") == True assertion would be broken).

    If you want all the assertions above to hold true, use this:

    class AttributeHider(object):
        def __get__(self, instance, owner):
            raise AttributeError("This is not the attribute you're looking for")
    
    class SpanishInquisition(Spam):
        expect = AttributeHider()
    
    assert hasattr(Spam, "expect") == True
    assert hasattr(SpanishInquisition, "expect") == False  # Works!
    assert hasattr(SpanishInquisition(), "expect") == False
    

    I believe this is the most elegant method, as the code is clear, generic and compact. Of course, one should really think twice if removing the attribute is what they really want.

  2. Overriding attribute lookup with __getattribute__ magic method. You can do this either in a subclass (or a mixin, like in the example below, as I wanted to write it just once), and that would hide attribute on the subclass instances. If you want to hide the method from the subclass as well, you need to use metaclasses.

    class ExpectMethodHider(object):
        def __getattribute__(self, name):
            if name == "expect":
                raise AttributeError("Nobody expects the Spanish Inquisition!")
            return super().__getattribute__(name)
    
    class ExpectMethodHidingMetaclass(ExpectMethodHider, type):
        pass
    
    # I've used Python 3.x here, thus the syntax.
    # For Python 2.x use __metaclass__ = ExpectMethodHidingMetaclass
    class SpanishInquisition(ExpectMethodHider, Spam,
                             metaclass=ExpectMethodHidingMetaclass):
        pass
    
    assert hasattr(Spam, "expect") == True
    assert hasattr(SpanishInquisition, "expect") == False
    assert hasattr(SpanishInquisition(), "expect") == False
    

    This looks worse (more verbose and less generic) than the method above, but one may consider this approach as well.

    Note, this does not work on special ("magic") methods (e.g. __len__), because those bypass __getproperty__. Check out Special Method Lookup section of the Python documentation for more details. If this is what you need to undo, just override it and call object's implementation, skipping the parent.

Needless to say, this only applies to the "new-style classes" (the ones that inherit from object), as magic methods and descriptor protocols aren't supported there. Hopefully, those are a thing of the past.

谎言 2024-11-15 21:20:14

也许您可以将 x 设置为 property 并在有人尝试访问它时引发 AttributeError。

>>> class C:
        x = 5

>>> class D(C):
        def foo(self):
             raise AttributeError
        x = property(foo)

>>> d = D()
>>> print(d.x)
File "<pyshell#17>", line 3, in foo
raise AttributeError
AttributeError

Maybe you could set x as property and raise AttributeError whenever someone try to access it.

>>> class C:
        x = 5

>>> class D(C):
        def foo(self):
             raise AttributeError
        x = property(foo)

>>> d = D()
>>> print(d.x)
File "<pyshell#17>", line 3, in foo
raise AttributeError
AttributeError
七月上 2024-11-15 21:20:14

仔细思考为什么要这样做;你可能不知道。考虑不要让 B 继承 A。

子类化的想法是专门化一个对象。特别是,类的子类应该是父类的有效实例:

>>> class foo(dict): pass
>>> isinstance(foo(), dict)
... True

如果您实现此行为(例如使用 x = property(lambda: AttributeError)),那么您就破坏了子类化概念,并且这是坏的。

Think carefully about why you want to do this; you probably don't. Consider not making B inherit from A.

The idea of subclassing is to specialise an object. In particular, children of a class should be valid instances of the parent class:

>>> class foo(dict): pass
>>> isinstance(foo(), dict)
... True

If you implement this behaviour (with e.g. x = property(lambda: AttributeError)), you are breaking the subclassing concept, and this is Bad.

爱给你人给你 2024-11-15 21:20:14

我也遇到了同样的问题,我认为我有充分的理由删除子类中的类属性:我的超类(称之为 A)有一个提供属性值的只读属性,但在我的子类(称之为 B),该属性是一个读/写实例变量。我发现 Python 正在调用属性函数,尽管我认为实例变量应该覆盖它。我本可以创建一个单独的 getter 函数来用于访问底层属性,但这似乎对接口命名空间造成不必要且不雅的混乱(就好像这真的很重要一样)。

事实证明,答案是创建一个新的抽象超类(称之为 S),具有 A 的原始公共属性,并让 A 和 B 派生自 S。由于 Python 有鸭子类型,所以 B 是否这样做并不重要不扩展 A,我仍然可以在相同的地方使用它们,因为它们隐式实现相同的接口。

I'm had the same problem as well, and I thought I had a valid reason to delete the class attribute in the subclass: my superclass (call it A) had a read-only property that provided the value of the attribute, but in my subclass (call it B), the attribute was a read/write instance variable. I found that Python was calling the property function even though I thought the instance variable should have been overriding it. I could have made a separate getter function to be used to access the underlying property, but that seemed like an unnecessary and inelegant cluttering of the interface namespace (as if that really matters).

As it turns out, the answer was to create a new abstract superclass (call it S) with the original common attributes of A, and have A and B derive from S. Since Python has duck typing, it does not really matter that B does not extend A, I can still use them in the same places, since they implicitly implement the same interface.

千笙结 2024-11-15 21:20:14

尝试这样做可能是一个坏主意,但是......

由于默认情况下查找 Bx 的工作方式,这似乎不是通过“正确的”继承来实现的。当获取 Bx 时,首先在 B 中查找 x,如果没有找到,则在 A 中搜索,但另一方面,当设置或删除Bx时,只会搜索B。例如,

>>> class A:
>>>     x = 5

>>> class B(A):
>>>    pass

>>> B.x
5

>>> del B.x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>  
AttributeError: class B has no attribute 'x'

>>> B.x = 6
>>> B.x
6

>>> del B.x
>>> B.x
5

在这里我们看到,首先我们似乎无法删除 Bx 因为它不存在(Ax 存在,并且是当您评估时得到的服务Bx)。然而,通过将 Bx 设置为 6,Bx 将存在,它可以通过 Bx 检索并通过 del Bx 删除这样它就不再存在,因此之后 Ax 将再次作为对 Bx 的响应。

另一方面,您可以做的是使用元类使Bx引发AttributeError

class NoX(type):
    @property
    def x(self):
        raise AttributeError("We don't like X")

class A(object):
    x = [42]

class B(A, metaclass=NoX):
    pass

print(A.x)
print(B.x)

现在纯粹主义者当然可能会大喊这破坏了LSP ,但事情没那么简单。这一切都归结为如果您认为您已经通过这样做创建了一个子类型。 issubclassisinstance 方法表示“是”,但 LSP 表示“否”(并且许多程序员会假设“是”,因为您是从 A 继承的)。

LSP 意味着如果 BA 的子类型,那么只要我们可以使用 A,我们就可以使用 B,但由于我们在执行此构造时无法执行此操作,因此我们可以得出结论,B 实际上不是 A 的子类型,因此没有违反 LSP。

Trying to do this is probably a bad idea, but...

It doesn't seem to be do this via "proper" inheritance because of how looking up B.x works by default. When getting B.x the x is first looked up in B and if it's not found there it's searched in A, but on the other hand when setting or deleting B.x only B will be searched. So for example

>>> class A:
>>>     x = 5

>>> class B(A):
>>>    pass

>>> B.x
5

>>> del B.x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>  
AttributeError: class B has no attribute 'x'

>>> B.x = 6
>>> B.x
6

>>> del B.x
>>> B.x
5

Here we see that first we doesn't seem to be able to delete B.x since it doesn't exist (A.x exists and is what gets served when you evaluate B.x). However by setting B.x to 6 the B.x will exist, it can be retrieved by B.x and deleted by del B.x by which it ceases to exist so after that again A.x will be served as response to B.x.

What you could do on the other hand is to use metaclasses to make B.x raise AttributeError:

class NoX(type):
    @property
    def x(self):
        raise AttributeError("We don't like X")

class A(object):
    x = [42]

class B(A, metaclass=NoX):
    pass

print(A.x)
print(B.x)

Now of course purists may yell that this breaks the LSP, but it's not that simple. It all boils down to if you consider that you've created a subtype by doing this. The issubclass and isinstance methods says yes, but LSP says no (and many programmers would assume "yes" since you inherit from A).

The LSP means that if B is a subtype of A then we could use B whenever we could use A, but since we can't do this while doing this construct we could conclude that B actually isn't a subtype of A and therefore LSP isn't violated.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文