I want to define some constants in class which will be define in class instance (derived classes) - how to signal error if this variables is not redefine in children classes? I want to raise NotImplementedError
on first read.
class Parent(object):
abstract_variable = ?
# I want achieve same behavior for variable
def abstract_function(self):
raise NotImplementedError()
class Child(Parent):
def __init__():
# should throw NotImplementedError() on read
print self.abstract_variable
Is it possible to do it in one line?
abstract_variable = ?
First of all, the clearest would be to not do anything in the parent class. Then when reading you'd just get an attribute error:
AttributeError: Child instance has no attribute 'abstract_variable'
Or in the parent class you can have a property
which raises NotImplementedError
and overwrite it with a property
with getter and setter in each child class; or in the child class set the value to None
in the class body...
However if you want to raise NotImplementedError
, you can make a non-data descriptor (that is, a descriptor class without __set__
, a property
always has __set__
). This allows you to set the value in the subclass.
The most straightforward way to do it is
class abstract_attribute(object):
def __get__(self, obj, type):
raise NotImplementedError("This attribute was not set in a subclass")
And you use it like
class Parent(object):
variable = abstract_attribute()
class Child(Parent):
def __init__(self):
try:
print(self.variable)
except Exception as e:
print("Got error %s" % e)
self.variable = 42
print(self.variable)
Child()
Which outputs
Got error This attribute was not set in a subclass
42
A property
does not make it possible to set the value as easily as with my abstract_attribute
.
But wait, we can make it a bit more magic: the descriptor can find out which attribute it was accessed from:
class abstract_attribute(object):
def __get__(self, obj, type):
# Now we will iterate over the names on the class,
# and all its superclasses, and try to find the attribute
# name for this descriptor
# traverse the parents in the method resolution order
for cls in type.__mro__:
# for each cls thus, see what attributes they set
for name, value in cls.__dict__.items():
# we found ourselves here
if value is self:
# if the property gets accessed as Child.variable,
# obj will be done. For this case
# If accessed as a_child.variable, the class Child is
# in the type, and a_child in the obj.
this_obj = obj if obj else type
raise NotImplementedError(
"%r does not have the attribute %r "
"(abstract from class %r)" %
(this_obj, name, cls.__name__))
# we did not find a match, should be rare, but prepare for it
raise NotImplementedError(
"%s does not set the abstract attribute <unknown>", type.__name__)
With this code, accessing self.variable
raises the exception with a very informative message:
NotImplementedError: <__main__.Child object at 0x7f7c7a5dd860> does not
have the attribute 'variable' (abstract from class 'Parent')
and Child.variable
gives
NotImplementedError: <class '__main__.Child'> does not have the
attribute 'variable' (abstract from class 'Parent')
Please see Antti Haapala's answer, he provides a better answer.
Use the property
function as a decorator
class Parent(object):
@property
def abstract_variable(self):
raise NotImplementedError()
Getting instance.abstract_variable
will then throw a NotImplementedError
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With