zoukankan      html  css  js  c++  java
  • python-property、__get__、__set__

    property

    property装饰器的应用来自这样一个问题:如果对实例的属性值不加以限制,那么实例的属性值会出现明显不合理的情况,为了解决这个问题也许你会思考在对属性的修改时利用实例方法加以限制,但python中引入了@property 装饰器更方便的解决这个问题。

    class Person(object):
        def __init__(self, age=1):
            self.age = age
    p = Person()
    p.age = -1  # 这显然是不合理的 
    
    class Person(object):
        def __init__(self):
            self._age = 1 # 私有属性,外部无法直接修改和访问
        
        def age_setter(self, age):
            if 0 < age < 120 and isinstance(age, int):
                self._age = age
            else:
                raise ValueError('age must between 0-120 and be integer')
    	def age_getter(self):
            return self._age
    # 这时虽然做到了属性值的控制,但是每次设置和获取值的时候要调用不同的方法,不太方便
    

    @property 装饰器兼顾了方便和控制,让实例的使用显得更加优雅,提高了可用性。另外,@property装饰器不设置setter时,就是一个只读属性,相当于对属性起到了保护作用,如下实验

    class Person(object):
        def __init__(self):
            self._height = 1 
    
        @property  # 这里就是height的getter
        def height(self):
            return self._height
    
        @height.setter
        def height(self, height):
            if 0 < height < 220 and isinstance(height, int):
                self._height = height
            else:
                raise ValueError('height must between 0-220 and be an integer')
    
        @property
        def normal_weight(self):
            return round(22.86*(self.height/100)**2*2, 2)
    
    p = Person()
    p.height = 190
    print(p.height)
    print(p.normal_weight)
    ------ 结果 ——————
    190
    165.05
    

    __set__ 和 _get_

    理解set和get方法,实际上必须知道描述器是什么,成为一个描述器,一个类必须至少有__get____set____delete__方法被实现。如果一个对象同时定义了 __get__()__set__(),它叫做资料描述器(data descriptor)。仅定义了 __get__() 的描述器叫非资料描述器(non-data descriptor)。

    __get__(self, obj, type=None) --> value
    定义了当描述器的值被取得的时候的行为。instance是拥有该描述器对象的一个实例。owner是拥有者本身
    __set__(self, obj, value) --> None
    定义了当描述器的值被改变的时候的行为。instance是拥有该描述器类的一个实例。value是要设置的值。
    __delete__(self, instance) --> None
    定义了当描述器的值被删除的时候的行为。instance是拥有该描述器对象的一个实例。
    

    几个注意事项:资料描述器的执行顺序优先于实例字典,而实例字典的执行顺序优先于非资料描述器,重写getattribute可能会阻止描述器的使用。关于属性查找优先顺序的问题,后面会写一篇博客描述和实验,这里不重复。

    class Descriptor:
        def __get__(self, instance, owner):
            print('1 get called,', 'instance is', instance, ',owner is', owner)
            return instance._a
    
        def __set__(self, instance, value):
            print('2 set called,', 'instance is', instance, ',value is', value)
            instance._a = value * 2
    
    class T:
        desc = Descriptor() # 类方法
        def __init__(self):
            self._a = 123
    
    t = T()
    t.desc = 5
    print('result:', t.desc)
    

    其实这里的使用已经很像@property了,但@property则更加简单方便。

    --------结果------------
    2 set called, instance is <__main__.T object at 0x00000182D90DC630> ,value is 5
    1 get called, instance is <__main__.T object at 0x00000182D90DC630> ,owner is <class '__main__.T'>
    result: 10
    

    描述器协议是一个理解python内部机制的知识点,属性(property), 方法(bound和unbound method), 静态方法和类方法都是基于描述器协议的。

    参考:

    https://pyzh.readthedocs.io/en/latest/Descriptor-HOW-TO-Guide.html

    https://blog.csdn.net/huithe/article/details/7484606


    作者:bitterz
    本文版权归作者和博客园所有,欢迎转载,转载请标明出处。
    如果您觉得本篇博文对您有所收获,请点击右下角的 [推荐],谢谢!
  • 相关阅读:
    greenlet和gevent模块的区别?
    Python整型int、浮点float常用方法
    jquery的相关应用
    mongoDB在java上面的应用
    SpringAOP的两种实现方式
    svn 服务器搭建 (Linux)
    maven的两种打包方式
    MongoDB 安装与配置
    springmvc拦截器配置
    Linux下安装mysql
  • 原文地址:https://www.cnblogs.com/bitterz/p/10237602.html
Copyright © 2011-2022 走看看