endeavour90
endeavour90

Reputation: 87

Defining @property

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

Answers (1)

Martijn Pieters
Martijn Pieters

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

Related Questions