Quantcast
Channel: Planet Python
Viewing all articles
Browse latest Browse all 22462

Lintel Technologies: How to create read only attributes and restrict setting attribute values on object in python

$
0
0

There are different way to prevent setting attributes and make attributes read only on object in python. We can use any one of the following way to  make attributes readonly

  1. Property Descriptor
  2. Using descriptor methods __get__ and __set__
  3. Using slots  (only restricts setting arbitary attributes)

Property Descriptor

Python ships with built in function called property. We can use this function to customize the way attributes be accessed and assigned.

First I will explain you about property before I get you idea about how it is useful to make attribute readonly.

Typical signature of the function property is

 property([fget[, fset[, fdel[, doc]]]]

As you can see here this function take four arguments, those are

fget is a function for getting an attribute value. fset is a function for setting an attribute value. fdel is a function for deleting an attribute value. And doc creates a docstring for the attribute.

All these function are for the sake of single attribute. That is fget function will be called when you access/get the attribute. fset function will be called when you are trying to set the attribute.

Simple example

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

    def getx(self):
        print "Getting attribute x"
        return self._x

    def setx(self, value):
        print "Setting attribute x"
        self._x = value

    def delx(self):
        print "Deleting attribue x"
        del self._x

    x = property(getx, setx, delx, "I'm the 'x' property.")

Instantiate Foo and try to play the instance attribute x

>>> i = Foo()
>>> i.x
Getting attribute x
>>> i.x = 3
Setting attribute x
>>> i.x 
Getting attribute x
>>> i._x #Still you can access hidden attrib _x Where it is abstracted as x
3
>>> del i.x
Deleting attribue x

I hope, you got what exactly the function property is and how we use it. In many cases we use this property to hide actual attributes and abstract them with another name.

You can use property as decorator also. Something like

class Foo(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

Now let’s come to actual thing how we make attribute readonly.

It’s simple you just don’t define setter for the property attribute. Let’s see the following example

class Bank(object):
    def __init__(self):
        self._money = 100000

    @property
    def money(self):
        """Get the money available."""
        return self._money

>>> b = Bank()
>>> b.money 
100000
>>> b.money = 9000000
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
>>> 
>>>
>>> del b.money
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
AttributeError: can't delete attribute

Here, as we didn’t define setter for the property attribute. So python won’t allow setting that specific attribute even you can’t delete if you don’t define fdel. Thus, attribute becomes read only. Still you can access b._money  and you can set that attribute there is no restriction over setting this internal attribute.

Descriptor methods __get__ and __set__

These magic methods define descriptor for the object attribute. To get complete understanding and usage about descriptor magic methods, please check other article .

Like fget and fset functions that property function takes, __get__ is used to define behavior when descriptor’s value is retrieved. __set__ method is used to define behavior  when descriptor value is getting set(assigned). Where __delete__ is used to define behavior when descriptor is getting deleted.

To restrict setting attribute and make it readonly. You have to use __set__ magic method of descriptor and raise exception in it.

Let’s see the simple example demonstrating descriptor object and readonly attributes using descriptors

class Distance(object):
    """Descriptor for a distance. Distance in meters"""

    def __init__(self, value=0.0):
        self.value = float(value)

    def __get__(self, instance, owner):
        return self.value

    def __set__(self, instance, value):
        self.value = float(value)


class Time(object):
    """Descriptor for a time."""

    def __init__(self, value=1.0):
        self.value = float(value)

    def __get__(self, instance, owner):
        return self.value

    def __set__(self, instance, value):
        self.value = float(value)

class Speed(object):
    """Descriptor for a speed."""

    def __get__(self, instance, owner):
        speed = instance.distance / instance.time
        return "%s m/s" % speed

    def __set__(self, instance, value):
        ## Restrict setting speed attribute
        raise AttributeError, "can not set attribute seepd"


class Vehicle(object):
    """
    Class to represent vehicle holding three descriptors for speed.
    Where speed is readonly
    """
    distance = Distance()
    time = Time()
    speed = Speed()

Lets see the result and trying to set speed attribute

>>> from python_property import Vehicle
>>> v = Vehicle()
>>> v.distance = 100
>>> v.time = 5
>>> v.speed
'20.0 m/s'
>>> v.speed = 40
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "python_property.py", line 98, in __set__
    raise AttributeError, "can not set attribute seepd"
AttributeError: can not set attribute seepd
>>>

As you can see here, we can’t set the attribute speed on instance v of Vehicle. Because we are restricting it in descriptor method __set__ of class Speed

Python __slots__

The basic usage of __slots__ is to save space in objects. Instead of having a dynamic dict that allows adding attributes to objects at anytime, there is a static structure which does not allow additions after creation. This will also gain us some performance due to lack of dynamic  attribute assignment. That is, it saves the overhead of one dict for every object that uses slots.

Think of you are creating lot of (hundreds, thousands) instances from the same class, this could be useful as memory and performance optimization tool.

If you are using __slots__ means you are defining static attributes on class. This is how we save memory and gain performance as there is not dynamic attribute assignment. Thus you can’t set new attributes on object.

>>> class Foo(object):
...     __slots__ = 'a', 'b'
... 
>>> i = Foo()
>>> i.a = 3
>>> i.c = 3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute 'c'
>>>

You see, in the above example we are not able to set attribute c as it not given in __sots__. Any way it’s about restricting  assignment to new attributes and you can combine either above two methods to make existing attributes readonly.

 

References:

[1] __get__ and __set__ data descriptors don’t work on instance attributes http://stackoverflow.com/questions/23309698/why-is-the-descriptor-not-getting-called-when-defined-as-instance-attribute

[2] http://stackoverflow.com/questions/472000/usage-of-slots

The post How to create read only attributes and restrict setting attribute values on object in python appeared first on Lintel Technologies Blog.


Viewing all articles
Browse latest Browse all 22462

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>