首页 > 编程语言 > 详细

编写高质量代码 改善Python程序的91个建议——笔记(三)

时间:2020-05-13 16:07:52      阅读:45      评论:0      收藏:0      [点我收藏+]

建议61:使用更为安全的property

  property是用来实现属性可管理性的built-in数据类型。它实际上是一种实现了__get__(), __set__()方法的类,用户也可以根据自己的需要定义个性化的property,其实质是一种特殊的数据描述符(数据描述符:如果一个对象同时定义了__get__()和__set__()方法,则称为数据描述符,如果仅定义了__get__()方法,则称为非数据描述符)。它和普通描述符的区别在于:普通描述符提供的是一种较为低级的控制属性访问的机制,而property是它的高级应用,它以标注库的形式提供描述符的实现,其签名形式为:

property(fget=None, fset=None, fdel=None, doc=None)  -> property attribute

  Property常见的使用形式有以下几种

  1)

class Some_Class(object):
    def __init__(self):
        self._somevalue = 0

    def get_value(self):
        print("calling get method to get 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‘m the ‘x‘ property.")

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

  2)

class Some_class(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_class()
obj.x = 10
print(obj.x)
del obj.x
print(obj.x)

  property的优势可言简单概括为以下几点:

  1)代码简洁,可读性更强。这条优势是显而易见的,显然obj.x += 1比 obj.set_value(ojb.get_value()+1)要更简洁易读,而且对于编程人员来说还少敲了几次键盘。

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

    创建一个property实际上就是将其属性的访问与特定的函数关联起来,相对于标准属性的访问,其工作原理如图。property的作用相当于一个分发器,对某个属性的访问并不直接操作具体对象,而对标准属性的访问没有中间这一层,直接访问存储属性的对象。

技术分享图片

 

 

   3)代码可维护性更好; 

  4)控制属性访问权限;提高数据安全性,如果用户想设置某个属性为只读:

class PropertyTest(object):
    def __init__(self):
        self.__varl = 20

    @property
    def x(self):
        return self.__varl

pt = PropertyTest()
print(pt.x)
pt.x = 12

  其实:使用property并不能真正完全达到属性只读的目的,正如以双下划线命令的变量并不是真正的私有变量一样,这些方法只是在直接修改属性这条道路上增加了一些障碍。如果用户想访问私有属性,同样能够实现,使用 pt._PropertyTest__varl = 30 来修改属性。

  property本质并不是函数,而是特殊类,既然是类的话,那么就可以被继承,因此用户便可以根据自己的需要定义property:

def update_meta(self, other):
    self.__name__ = other.__name__
    self.__doc__ = other.__doc__
    self.__dict__.update(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 attribute using fdel.__name__")
                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.setx 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)

  回到前面的问题:使用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._ROClass__name = "modify"
print(a.__dict__)
print(ROClass.__dict__)
print(a.name)

   a._ROClass__name = "modify" 创建了新的属性,这样就能很好的保护可读属性不被修改。

 

编写高质量代码 改善Python程序的91个建议——笔记(三)

原文:https://www.cnblogs.com/xiangxiaolin/p/12881982.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!