Reputation: 6239
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.
Upvotes: 11
Views: 5569
Reputation: 91
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.
Upvotes: 9
Reputation: 46
You can do some like this, parent class:
class Parent(object):
__metaclass__ = ABCMeta
@abstractproperty
def name(self):
pass
Child class:
class Child(Parent):
name = None
def __init__(self):
self.name = 'test'
Now
obj = Child()
obj.name
gives the required output of 'test'.
By doing this you can enforce a class to set an attribute of parent class and in child class you can set them from a method.
It is not a perfect solution that you require, but it is close. The only problem with this solution is you will need to define all the abstractproperty of parent as None
in child class and them set them using a method.
Upvotes: 3
Reputation: 9072
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.
Upvotes: 5