Python: deleting a class attribute in a subclass
Solution 1
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.
Solution 2
You can use delattr(class, field_name)
to remove it from the class definition.
Solution 3
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?).
Solution 4
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:
-
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.
-
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 callobject
'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.
Solution 5
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
Comments
-
Ram Rachum almost 2 years
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?
-
sage over 10 yearsI'm not subclassing - I'm deleting a setting from a settings class in my test to check correct error handling - so this is exactly what I need!
-
Cecil Curry over 8 yearsI generally hate non-answers reducing to "Don't do this awesome and interesting thing, because I'm a responsible adult and... <hand-waving>!" This is no exception. While preserving Liskov substitutibility is vital to sane development, every rule was intended to be broken. We're all adults, here. Thanks.
-
Cecil Curry over 8 years
-
Keith over 8 years@CecilCurry the deeper question is why does the OP think he needs to delete it? Anyway, it's a bad design.
-
Dave about 8 years@Keith, perhaps the base class was poorly designed and the user wants to fix it without copying the entire source code. As an example, Django's AuthenticationForm is supposedly a "base class for authenticating users", but it defines username and password form fields, which makes it useless for non-password authentication. Deleting those attributes preserves all of the base class' methods for reuse while getting rid of the junk that shouldn't have been in a base class in the first place.
-
broccoli2000 almost 8 yearsIt would be helpful to have a fully coded example. Can this be filled out? I'm not sure where this goes in the code.
-
RayLuo over 7 yearsI generally agree with @CecilCurry , because I encountered similar answers to my other questions before, and I hate them. Yet in this particular case I would say deleting a parent attribute is not awesome and interesting in the first place, so a kindly reminder of LSP is not nonsense. I ended up upvoting both this answer and CecilCurry 's comment.
-
mehmet over 7 yearsI find answers pointing out to widely accepted principals of programming very beneficial, because I am learning. I also know every rule has some sort of flexibility so take them as guidelines.
-
drdaeman about 7 yearsThis is the only actually working solution at the moment. Except, of course, that it "removes" attributes only on subclass instances, and not on the subclasses themselves.
-
DylanYoung about 3 years@broccoli2000 Anywhere you want as long as it's before you need to use the class.