Use of property management

preface

Property is a bulit in data type used to realize the manageability of properties (Note: property is called a function in many places, which I don't think is very appropriate. In fact, it is a class that implements _get__ () and set() methods, and users can define properties according to their own needs), Its essence is a special data descriptor (data descriptor: if an object defines both _ get and set methods, it becomes a data descriptor. If only get methods are defined, it is called a non data descriptor). The difference between it and ordinary data descriptors is that it provides an advanced mechanism to control attribute access. It provides the implementation of descriptors in the form of annotation library.

1, Two ways to use property

1. The first form:

class Some_Class(object):
    def __init__(self):
        self._somevalue = 0
    def get_value(self):
        print ('calling get method to return value')
        return self._somevalue
    def set_value(self,value):
        print ('calling set method to set value')
        self._somevalue = value
    def del_attr(self):
        print ('calling delete method to delete value')
        del self._somevalue
    x = property(get_value,set_value,del_attr,'i am the property')
    
obj = Some_Class()
obj.x = 10
print (obj.x+2)
del obj.x
obj.x

Output:

calling set method to set value
calling get method to return value
12
calling delete method to delete value
calling get method to return value
...
AttributeError: 'Some_Class' object has no attribute '_somevalue'

2. The second form:

class Some_property(object):
    _x = None
    def __init__(self):
        self._x = None
    @property
    def x(self):
        print ('calling get method to return value')
        return self._x
    @x.setter
    def x(self,value):
        print ('calling set method to set value')
        self._x = value
    @x.deleter
    def x(self):
        print ('calling delete method to delete value')
        del self._x

obj = Some_property()
obj.x = 10
print (obj.x+2)
del obj.x
print (obj.x)

Output:

calling set method to set value
calling get method to return value
12
calling delete method to delete value
calling get method to return value
None

2, Advantages of property

1. The code is more concise and readable

For example: obj X + = 1 ratio obj set_ Value (obj. Get_value() + 1) should be simpler and easier to read.

2. Better management of attribute access

Property directly turns the access to the property into the call of the corresponding get, set and other related functions, and the property can be better managed and controlled. As follows:

class Date:
    def __init__(self, year, month, day):
        self.year = str(year)
        self.month = str(month)
        self.day = str(day)

    def get_date(self):
        return self.year + '-' + self.month + '-' + self.day

    def set_date(self, date_as_string):
        year, month, day = date_as_string.split('-')
        if not (2000 <= int(year) <= 2021 and 0 <= int(month) <= 12 and 0 <= int(day) <= 31):
            print('date.value error')
            raise AssertionError
        self.year = year
        self.month = month
        self.day = day

    date = property(get_date, set_date)
 
d1 = Date(2009,10,8)
print(d1.get_date())
d1.set_date('2000-5-1')
d1.get_date()

Output:

2009-10-8
'2000-5-1'

3. Better code maintainability

property repackages properties and presents them to users in the form of an interface. Properties are accessed in a unified syntax. When the specific implementation needs to be changed, the access methods can still be kept consistent. For example, in the above example, if you change the display mode of date, such as' August 5, 2021 ', you only need to change get_value can be changed accordingly. The external access to date does not need to be changed, so the maintainability of the code is greatly improved.

4. Control attribute access rights to improve data security

If the user sets a property as read-only, let's see how to use property

class PropertyTest(object):
    def __init__(self):
        self.__var1 = 20
    @property
    def x(self):
        return self.__var1
        
pt = PropertyTest()
print (pt.x)
pt.x = 12

Output:

20-------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
in ()
7 pt = PropertyTest()
8 print (pt.x)
----> 9 pt.x = 12
AttributeError: can't set attribute

In fact, we can modify its attribute value:

pt._PropertyTest__var1 = 30
print (pt.x)

30

How to truly implement the read-only property?

def ro_property(obj,name,value):
    setattr(obj.__class__,name,property(lambda obj:obj.__dict__['__'+name]))
    setattr(obj,'__'+name,value)
class ROClass(object):
    def __init__(self,name,available):
        ro_property(self,'name',name)
        self.available = available
a = ROClass('read only',True)
print (a.name)
a._Article__name = 'modify'
print (a.__dict__)
print (ROClass.__dict__)
print (a.name)

Output:

read only
{'__name': 'read only', 'available': True, '_Article__name': 'modify'}
{'module': 'main', 'init': <function ROClass.init at 0x000001CD34EC0048>, 'dict': <attribute 'dict' of 'ROClass' objects>, 'weakref': <attribute 'weakref' of 'ROClass' objects>, 'doc': None, 'name': <property object at 0x000001CD34EFDE58>}
read only

3, Customize the property according to the requirements

def update_meta(self,other):
    self.__name__ = other.__name__
    self.__doc__ = other.__doc__
    self.__dict__ = other.__dict__
    return self
class UserProperty(property):
    def __new__(cls,fget = None,fset= None,fdel =None,doc=None):
        if fget is not None:
            def __get__(obj,objtype=None,name=fget.__name__):
                fget = getattr(obj,name)
                print ('fget name:'+fget.__name__)
                return fget()
            fget = update_meta(__get__,fget)
        if fset is not None:
            def __set__(obj,value,name=fset.__name__):
                fset = getattr(obj,name)
                print ('fset name:',fset.__name__)
                print ('setting value:',str(value))
                return fset(value)
            fset = update_meta(__set__,fset)
        if fdel is not None:
            def __delete__(obj,name=fdel.__name__):
                fdel = getattr(obj,name)
                print ('warning you are deleting attr useing fdel.__del__')
                return fdel
            fdel = update_meta(__delete__,fdel)
        return property(fget,fset,fdel,doc)
class C(object):
    def get(self):
        print ('calling C.getx to get value')
        return self._x
    def set(self,x):
        print ('calling C.sets to set value')
        self._x = x
    def delete(self):
        print ('calling c.delx to delete value')
        del self._x
    x = UserProperty(get,set,delete)
    
c = C()
c.x = 1
print (c.x)
del c.x

Output:

fset name: set
setting value: 1
calling C.sets to set value
fget name:get
calling C.getx to get value
1
warning you are deleting attr useing fdel.del

In the above example, UserProperty inherits property and its constructor__ new__(cls,fget=None,fset=None,fdel=None,doc=None) redefines the fget(), fset(), fdel() methods to meet the specific needs of users. The last returned object is actually an instance of property, so users can use UserProperty like using property.

Keywords: Python property

Added by El Ornitorrico on Tue, 04 Jan 2022 17:40:50 +0200