Question

Getting up to speed on learning classes. I have been reading that the constructor (def init in Python) should only set assigned variables, that calculated instance attributes should be set via a property. Also, that using @property is preferred to a Java-style getter/setter.

OK, but every example I have ever seen on this sets only one property. Let's say I have an object with three complicated attributes that need to be calculated, queried etc. How do you represent multiple @property getters, setters, deleters? Here is an example from another post:

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

So if I had three instance variables that were calculated values based on some other attributes, would it look like this

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

    @property
    def y(self):
        """I'm the 'y' property."""
        return self._y

    @y.setter
    def y(self, value):
        self._y = value

    @y.deleter
    def y(self):
        del self._y

    @property
    def z(self):
        """I'm the 'z' property."""
        return self._z

    @z.setter
    def z(self, value):
        self._z = value

    @z.deleter
    def z(self):
        del self._z

Or is that fact that I only ever see one @property statement mean that having a class with more than one @property is a bad idea?

Was it helpful?

Solution

No, you can use multiple @property decorators to your heart's content. There is no limit here, other than that of example writers imaginations, apparently.

The Python standard library is full of @property use if you want examples:

  • numbers defines ABCs for the numbers classes in Python.

  • tempfile implements temporary file objects

  • threading provding higher-level thread support

  • urlparse for handling URLs and query strings.

etc.

You had it spot on; multiple properties would look exactly like what you posted.

OTHER TIPS

it's not a bad idea. it's a regular property pattern.

another pattern is doing like:

class A(object):

    def _get_a(self):
        return self._a

    def _set_a(self, v)
        self._a = v

    def _del_a(self)
        del self._a

    a = property(_get_a, _set_a, _del_a)

the result is the same that in the first example, and you can use it as much as you wish. initializing self._a in the constructor is a good idea, since otherwise, accesing it BEFORE by calling self.a would raise AttributeError

To bounce on another answer,

there is no shortcut to the somewhat ugly looking pattern

Not in python stdlib indeed, but there is now a library named pyfields to do this in a more concise way, without sacrificing speed when you do not need validators not converters.

from pyfields import field, init_fields

class C(object):
    x = field(default=None, doc="the optional 'x' property")
    y = field(doc="the mandatory 'y' property")
    z = field(doc="the mandatory 'z' property")

    @init_fields
    def __init__(self):
        pass

c = C(y=1, z=2)
print(vars(c))

yields

{'z': 2, 'y': 1, 'x': None}

I'm the author of this lib, I wrote it because none of the existing alternatives were satisfying to me.

See documentation for details - Do not hesitate to provide feedback !

It may be ugly, but that is the way it works. In general, the pythonic recommendation is not to create simple setter/getters that directly access the native variable (unless you know the interface is likely to change). Assuming that the reason for the properties is 'complex calculations' as you said, there is no shortcut to the somewhat ugly looking pattern. See this link http://tomayko.com/writings/getters-setters-fuxors

yes it does but @property decorator make it more neat and tidy to define multiple property in a class. Property is Different with attribute. The reason property used in encapsulation because python don't have built-in access modifier. In fact, when we call @property decorator above a method we instantiate a property object. Property object is an object have get,set, and del method. Property can be defined in a class without the need to define it in init. init method invoked when an object instantiated. If the private attribute need to set when the object created define it in init but when we don't need initial value we have no need to define the attributes. Here's the example of property without attribute.

class C(object):
#X Property
@property
def x(self):
    """I'm the 'x' property."""
    print('Get The X Property')
    return self._x

@x.setter
def x(self, value):
    print('X Property Setted to {}'.format(value))
    self._x = value

@x.deleter
def x(self):
    print('X is Killed !')
    del self._x

#Y Property
@property
def y(self):
    """I'm the 'y' property."""
    print('Get The Y Property')
    return self._y

@y.setter
def y(self, value):
    print('Y Property Setted to {} '.format(value))
    self._y = value

@y.deleter
def y(self):
    print('Y is Killed !')
    del self._y

When we need to define x and y as an attribute to the class C. Just set the the attribute in init. so, x and y listed as class C attribute and have initial value setted when the object instantiated.

class C(object):
def __init__(self):
    self._x = 0
    self._y = 0

#X Property
@property
def x(self):
    """I'm the 'x' property."""
    print('Get The X Property')
    return self._x

@x.setter
def x(self, value):
    print('X Property Setted to {}'.format(value))
    self._x = value

@x.deleter
def x(self):
    print('X is Killed !')
    del self._x

#Y Property
@property
def y(self):
    """I'm the 'y' property."""
    print('Get The Y Property')
    return self._y

@y.setter
def y(self, value):
    print('Y Property Setted to {} '.format(value))
    self._y = value

@y.deleter
def y(self):
    print('Y is Killed !')
    del self._y

The Difference is simple. Without any initial value the class C have no x and y attributes. so, if we trying to get the attribute an exception will raised. With initial value the class C already have X and Y attribute from the beginning.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top