I want to enforce a Child class to set several attributes(instance variables), to achieve I am using abstractproperty decorator in python 2.7. Here is the sample code:
from abc import ABCMeta, abstractproperty
class Parent(object):
__metaclass__ = ABCMeta
@abstractproperty
def name(self):
pass
class Child1(Parent):
pass
class Child2(Parent):
name = 'test'
class Child3(Parent):
def __init__(self):
self.name = 'test'
obj_child1 = Child1()
Child1 object creation gives an error as expected.
obj_child2 = Child2()
Child2 object creation works fine as because abstract attribute 'name' is set
However
obj_child3 = Child3()
gives TypeError: Can't instantiate abstract class Child3 with abstract methods name
I did not understand:although the attribute 'name' is set in the init method, why the Child3 object creation is throwing type error.
My requirement is set attributes inside a method of child class. An explanation of what is wrong with child2 class and if there is a better way to enforce a child class to set attributes in a method will help me. Thanks in advance.
Use dot notation or setattr() function to set the value of a class attribute. Python is a dynamic language. Therefore, you can assign a class variable to a class at runtime. Python stores class variables in the __dict__ attribute.
In Python, property() is a built-in function that creates and returns a property object. The syntax of this function is: property(fget=None, fset=None, fdel=None, doc=None)
Here's what I believe is happening.
When you try to instantiate Child1
, you have not provided an implementation for the abstract property name
. You may have done so like this:
class Child1(Parent):
@property
def name(self):
return "foo"
Since the class does not provide an implementation for all abstract methods inherited from its parents, it is effectively an abstract class itself, and therefore cannot be instantiated. Trying to instantiate it gives you your TypeError
.
In the case of Child2
, you define it as:
class Child2(Parent):
name = 'test'
In this case, you're overwriting the name
property of the class so that it's no longer referencing an abstract property that needs to be implemented. This means that Child2
is not abstract and can be instantiated.
One difference I noticed was that when name = 'test'
as you've implemented it, vars(Child2)
returns output like this:
{..., '__abstractmethods__': frozenset(), ..., 'name': 'test'}
However, when you change this to something like foo = 'test'
, you get this:
{..., '__abstractmethods__': frozenset({'name'}), ..., 'foo': 'test'}
Notice that the __abstractmethods__
property is an empty set in the case that you define name = 'test'
i.e. when Child2
can be instantiated.
Finally, in the case of Child3
you have to bear in mind that the abstract property is stored as a property of the class itself, which you don't redefine anywhere.
As soon as you try to create an instance of it, the interpreter will see that it's missing an implementation for at least one abstract method and throw the TypeError
you see. It doesn't even reach the assignment self.name = 'test'
in the constructor.
To answer your second part about how to enforce children to always provide an implementation for abstract properties/methods when they can do something like you did - I'm not actually sure if it's possible. Python is a language of consenting adults.
A simpler solution will be
class Parent(object):
name = None
def __init__(self):
if self.name == None:
raise NotImplementedError('Subclasses must define bar')
class Child1(Parent):
pass
class Child2(Parent):
name = 'test'
class Child3(Parent):
def __init__(self)
self.name = 'test'
obj1 = Child1()
raises NotImplementedError
obj2 = Child2()
works fine
obj3 = Child3()
works fine. This is what you need to enforce a child class to set attribute name
and set the attribute from a method.
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