0

I have this

#!/usr/bin/env python

import math
class myclass(object):

    def __init__(self,radius):
            self.radius = radius
    @property
    def area(self):
            print myclass.area,  type(myclass.area)
            return math.pi * ( self.radius ** 2)

    @area.setter
    def area(self,value):
           print myclass.area, type(myclass.area)
           pass

    @area.deleter
    def area(self):
           print myclass.area, type(myclass.area)        
           del myclass.area                


if __name__ == '__main__':

    c = myclass(5.4)
    c.area
    c.area = 65
    del c.area                                  

This gives:

$ ./propertytest.py 
<property object at 0x7ff0426ac0a8> <type 'property'>
<property object at 0x7ff0426ac0a8> <type 'property'>
<property object at 0x7ff0426ac0a8> <type 'property'>

Question:

Look at the way property object area has been accessed: c.area. area appears on the right side of the dot operator. Which special method is used by the property object to bind the class instance object with the right instance method and compute the result? How do properties work?

2
  • You are using old-style classes. Add in the required super(myclass, self).__init__(). Commented Aug 20, 2013 at 14:56
  • 2
    @Wessie This inherits from object, and thus is a new-style class. Commented Aug 20, 2013 at 15:10

2 Answers 2

4

Properties are descriptors. A descriptor is an instance of a class with __get__, __set__ and/or __delete__ method. Whenever python does getattr, setattr and delattr, if the instance does not have said attribute, and in the class there is a attribute with that name, that has the matching magic method, the magic method is called instead of the reading/writing/deleting the attribute.

The Python documentation tells more on descriptors and also has this pure python emulation of builtin property type:

class Property(object):
    "Emulate PyProperty_Type() in Objects/descrobject.c"

    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        if doc is None and fget is not None:
            doc = fget.__doc__
        self.__doc__ = doc

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        if self.fget is None:
            raise AttributeError("unreadable attribute")
        return self.fget(obj)

    def __set__(self, obj, value):
        if self.fset is None:
            raise AttributeError("can't set attribute")
        self.fset(obj, value)

    def __delete__(self, obj):
        if self.fdel is None:
            raise AttributeError("can't delete attribute")
        self.fdel(obj)

    def getter(self, fget):
        return type(self)(fget, self.fset, self.fdel, self.__doc__)

    def setter(self, fset):
        return type(self)(self.fget, fset, self.fdel, self.__doc__)

    def deleter(self, fdel):
        return type(self)(self.fget, self.fset, fdel, self.__doc__)
Sign up to request clarification or add additional context in comments.

Comments

1

To make what is happening above a bit more explicit, let's get rid of the decorator syntax sugar:

import math
class myclass(object):

    def __init__(self,radius):
            self.radius = radius

    def getter(self):
            print myclass.area,  type(myclass.area)
            return math.pi * ( self.radius ** 2)

    # create a property object with "getter" as the getter, assigned to "area"
    area = property(getter) 

    # area.__get__ is now a method that wraps "getter"



    def setter(self,value):
           print myclass.area, type(myclass.area)
           pass

    # create a copy of the "area" object with "setter" as a setter, assign back to "area"
    area = area.setter(setter) 

    # area.__set__ is now a method that wraps "setter"



    def deleter(self):
           print myclass.area, type(myclass.area)        
           del myclass.area  

    # create a copy of the "area" object with "deleter" as a deleter, assign back to "area"
    area = area.deleter(deleter)

    # area.__delete__ is now a method that wraps "deleter"

19 Comments

The code gives an error when run. AttributeError: can't set attribute
@abc Seems to be running correctly on my shell and in ideone: ideone.com/GmFfDm
@abc Here it is again with the code after the class definition: ideone.com/jpw2CT
@abc The docs do say that. To quote this page: "A property object has getter, setter, and deleter methods usable as decorators that create a copy of the property with the corresponding accessor function set to the decorated function."
I have that in my answer
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.