Python的特性(Property)和描述符

Posted BlackMatrix

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python的特性(Property)和描述符相关的知识,希望对你有一定的参考价值。

特性(Property)

 

特性是对类的一个特定属性进行拦截,在操作这个属性时,执行特定的函数,对属性的操作进行拦截。

 

特性的实现

特性使用property类来实现,也可以使用property装饰器实现,二者本质是一样的。

property类的__init__函数接收4个参数,来实现属性的获取、赋值、删除及文档。

    def __init__(self, fget=None, fset=None, fdel=None, doc=None): # known special case of property.__init__
        """
        property(fget=None, fset=None, fdel=None, doc=None) -> property attribute
        
        fget is a function to be used for getting an attribute value, and likewise
        fset is a function for setting, and fdel a function for del‘ing, an
        attribute.  Typical use is to define a managed attribute x:
        
        class C(object):
            def getx(self): return self._x
            def setx(self, value): self._x = value
            def delx(self): del self._x
            x = property(getx, setx, delx, "I‘m the ‘x‘ property.")
        
        Decorators make defining new properties or modifying existing ones easy:
        
        class C(object):
            @property
            def x(self):
                "I am the ‘x‘ property."
                return self._x
            @x.setter
            def x(self, value):
                self._x = value
            @x.deleter
            def x(self):
                del self._x
        
        # (copied from class doc)
        """
        pass

从代码上看,4个参数都不是必须的,如果没有传入对应的操作函数,则取默认值None,则对应的操作不受支持,试图调用默认值None时,会引发异常。

测试代码:

class Man(object):

    def __init__(self, age):
        self._age = age

    @property
    def age(self):
        return self._age

    @age.getter
    def age(self):
        return self._age


if __name__ == __main__:

    tom = Man(22)
    print(tom.age)
    tom.age = 23
    print(tom.age)

因为缺少setter的函数方法,所以试图给age赋值时,会引发异常。

AttributeError: cant set attribute

age()方法上增加@property装饰器,实际上等同于age = property(),将age赋值为property的实例。

经过测试,把

@property
def age(self):
    pass

修改为

age = property()

不影响代码的执行,所以@age.setter装饰器就很好理解了,因为age是property类的实例,property类有setter的方法,age继承了property类的setter方法,所以可以使用@age.setter装饰器。其他的setter、getter、deleter也是同理。

 

特性的继承

类的实例和子类都会继承类的特性,测试代码:

class Person(object):
  def
__init__(self, age): self._age = age @property def age(self): return self._age @age.setter def age(self, value): self._age = value * 2 @age.getter def age(self): return self._age class Man(Person): pass if __name__ == __main__: tom = Man(22) print(tom.age) tom.age = 23 print(tom.age)

在age的setter方法中,将age的值乘以2,从运行结果上看,子类及其实例都是继承property的

22
46

 

描述符

任何带有如下方法的类都可以作为描述符

def __get__(self, instance, owner):
    ...

def __set__(self, instance, value):
    ...

def __delete__(self, instance):
    ....

 

__get__方法接收三个参数:self为描述符实例自身; instance指访问属性所属的实例,当访问的属性为类属性时,instance为None;owner指描述符实例附加到的类。

测试代码:

class Descriptor:

    def __init__(self):
        pass

    def __get__(self, instance, owner):
        print(self, instance, owner)

    def __set__(self, instance, value):
        print(self, instance, value)

    def __delete__(self, instance):
        print(self, instance)


class ClassA(object):

    a = Descriptor()


if __name__ == __main__:
    ClassA.a
    x = ClassA()
    x.a

从运行结果可以看出,当访问类属性时,instance为None;当访问实例属性时,instance为属性所属的的实例。

instance属性为None时,描述符对象会直接访问所附加到的类的类属性。当instance属性不为None时,描述符会直接访问实例属性。

<__main__.Descriptor object at 0x1081413c8> None <class __main__.ClassA>
<__main__.Descriptor object at 0x1081413c8> <__main__.ClassA object at 0x108141358> <class __main__.ClassA>

__set__方法的第三个参数value为需要赋值的属性,__delete__方法的两个参数与__get__方法的前两个参数相同。

 

以上是关于Python的特性(Property)和描述符的主要内容,如果未能解决你的问题,请参考以下文章

Python描述符(descriptor)解密

python 描述符 上下文管理协议 类装饰器 property metaclass

Python描述符(descriptor)解密(转)

python2.7高级编程 笔记二(Python中的描述符)

Python学习:17.Python面向对象(属性(特性),成员修饰符,类的特殊成员)

Python描写叙述符(descriptor)解密