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.