属性管理property的使用


前言

property是用来实现属性可管理性的bulit-in数据类型(注意:很多地方将property称为函数,个人认为不是很恰当,它实际上是一种实现了__get__()、set()方法的类,用户可以根据自己的需要个性化定义property),其实质是一种特殊的数据描述符(数据描述符:如果一个对象同时定义了__get__和__set__方法,则成为数据描述符,如果仅定义了__get__方法,则称为非数据描述符)。它和普通数据描述符的区别是,它提供了一种高级的控制属性访问的机制,它以标注库的形式提供描述符的实现。

一、property的两种使用方法

1. 第一种形式:

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

输出:

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. 第二种形式:

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)

输出:

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

二、property的优点

1.代码更简洁,可读性更强

如:obj.x += 1 比 obj.set_value(obj.get_value() + 1)要更简洁和易读。

2.更好的管理属性访问

property将对属性的访问直接转为对应的get、set等相关函数的调用,属性能够更好的被管理和控制。如下:

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 1 <= int(month) <= 12 and 1 <= 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()

输出:

2009-10-8
‘2000-5-1’

3.代码可维护性更好

property对属性进行再包装,以类似于接口的形式呈现给用户,以统一的语法来访问属性,当具体实现需要改变的时候,访问的方式仍然可以保留一致。例如上面例子中,如果更改date的显示方式,如‘2021年8月5号’,则只需要对get_value做对应的更改即可,外部访问date的方式不需要改变,因此代码的可维护性大大提高。

4.控制属性访问权限,提高数据安全性

如果用户像设置某个属性为只读,来看看如何使用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

输出:

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

其实我们还是可以修改其属性值的:

pt._PropertyTest__var1 = 30
print (pt.x)

30

如何真正的实现属性的只读呢?

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)

输出:

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

三、根据需求,自定property

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

输出:

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

上述例子中,UserProperty继承property,其构造函数__new__(cls,fget=None,fset=None,fdel=None,doc=None)中重新定义了fget()、fset()、fdel()方法以满足用户特定的需要,最后返回的对象实际还是property的实例,因此用户可以能够想使用property一样使用UserProperty。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值