简体   繁体   中英

How does python property class instance work?

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?

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__)

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"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM