Reputation: 87
I have problem in defining a getter by using @property
in concrete class. Here is the python code:
from abc import ABCMeta, abstractproperty
class abstract(object):
__metaclass__ = ABCMeta
def __init__(self, value1):
self.v1 = value1
@abstractproperty
def v1(self):
pass
class concrete(abstract):
def __init__(self, value1):
super(concrete, self).__init__(value1)
@property
def v1(self):
return self.v1
Then test the code and get this error:
test_concrete = concrete("test1")
test_concrete.v1
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-190-eb896d7ec1c9> in <module>()
----> 1 test_concrete = concrete("test1")
2 test_concrete.v1
<ipython-input-189-bf15021022d3> in __init__(self, value1)
11 class concrete(abstract):
12 def __init__(self, value1):
---> 13 super(concrete, self).__init__(value1)
14
15 @property
<ipython-input-189-bf15021022d3> in __init__(self, value1)
3
4 def __init__(self, value1):
----> 5 self.v1 = value1
6
7 @abstractproperty
AttributeError: can't set attribute
Basically I intend to set v1
as a read-only attribute.
I am used to using the same name for the attribute and its getter function, but it seems that I cannot do it if the class inherited from an abstract class.
I found a workaround by using different name for the getter function v1
as get_v1
:
class abstract(object):
__metaclass__ = ABCMeta
def __init__(self, value1):
self.v1 = value1
@abstractproperty
def get_v1(self):
pass
class concrete(abstract):
def __init__(self, value1):
super(concrete, self).__init__(value1)
@property
def get_v1(self):
return self.v1
Is there a better workaround for this problem?
Upvotes: 1
Views: 104
Reputation: 1121266
Your issue has nothing to do with abstract classes. This is a namespace issue; the property object and instance attributes occupy the same namespace, you cannot have both an instance attribute and a property use the exact same name.
You are trying to access the property by using self.v1 = ...
and v1
is not magically an instance attribute just because you are accessing it from a method on the instance, that is always going to be the property object.
In the same vein, your property getter will cause an infinite loop:
@property
def v1(self):
return self.v1
because self.v1
is the property object, not an instance attribute.
Use a different name for the actual storage:
def __init__(self, value1):
self._v1 = value1
@property
def v1(self):
return self._v1
Note that you cannot have truly private attributes in Python (like you can in C# or Java); the leading underscore on names is only a convention and a determined coder can always access your instance state directly. A property
object does not change this.
Upvotes: 2