美文网首页
Python基础27-面向对象(系统内置方法7-描述器)

Python基础27-面向对象(系统内置方法7-描述器)

作者: Jacob_LJ | 来源:发表于2018-05-23 23:00 被阅读7次

Python基础-面向对象(方法)

7 描述器

1 概念

  • 用于描述一个属性对应操作的对象。
  • 属性对应操作一般为:增/改、删、查

2 作用

  • 可以代为管理一个类属性的读写删操作, 在相关方法中, 对数据进行验证处理, 过滤处理等等
  • 如果一个类属性被定义为描述器,那么以后对这个类属性的操作(读写删), 都将由这个描述器代理

3 定义

3.1 通过 property创建属性的描述器

其实就是前面提到的 property 的使用

  • 一般我们定义类都会将属性定义为私有属性,这样外界就不能随便访问或赋值
class Person:
    def __init__(self, value):
        self.__age = value
  • 让实例能够通过.age方式进行访问或过滤性修改
  • 通过 property 定义后返回的 age 就是一个描述器,描述器就是一个对属性操作进行描述(即过滤或保护等)的对象

class Person:
    def __init__(self, value):
        self.__age = value

    def get_age(self):
        print("get age")
        return self.__age

    def set_age(self, value):
        print("set age")
        if value < 0:
            value = 0
        self.__age = value

    def del_age(self):
        print("del age")
        del self.__age

    # 此时返回的 age 就是一个描述器,描述器就是一个对属性操作进行描述(即过滤或保护等)的对象
    age = property(get_age, set_age, del_age)

p = Person()
# 访问
print(p.age)
# 赋值
p.age = 19
# 删除
del p.age

  • 查看上面代码中通过property 定义的 age 描述器与 name 属性分区对别
    age 是被分配到 Data descriptors defined here: 区
class Person:
    # 与 age 对比用
    name = "fkm"

    def __init__(self, value):
        self.__age = value

    def get_age(self):
        print("get age")
        return self.__age

    def set_age(self, value):
        print("set age")
        if value < 0:
            value = 0
        self.__age = value

    def del_age(self):
        print("del age")
        del self.__age

    age = property(get_age, set_age, del_age)

p = Person(10)
print(Person.__dict__)
print(p.__dict__)

print("-" * 20)

help(Person)



>>>> 打印结果

{
'__module__': '__main__', 
'name': 'fkm', 
'__init__': <function Person.__init__ at 0x10c86ebf8>, 
'get_age': <function Person.get_age at 0x10c86eb70>, 
'set_age': <function Person.set_age at 0x10c86ec80>, 
'del_age': <function Person.del_age at 0x10c86ed08>, 
'age': <property object at 0x10c6bea98>, 
'__dict__': <attribute '__dict__' of 'Person' objects>, 
'__weakref__': <attribute '__weakref__' of 'Person' objects>, 
'__doc__': None
}

{'_Person__age': 10}
--------------------
Help on class Person in module __main__:

class Person(builtins.object)
 |  Methods defined here:
 |  
 |  __init__(self, value)
 |      Initialize self.  See help(type(self)) for accurate signature.
 |  
 |  del_age(self)
 |  
 |  get_age(self)
 |  
 |  set_age(self, value)
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)
 |  
 |  age
 |  
 |  ----------------------------------------------------------------------
 |  Data and other attributes defined here:
 |  
 |  name = 'fkm'


Process finished with exit code 0

  • 使用 property 内置方式,同样可以通过属性描述器访问属性
class Person:
    def __init__(self):
        self.__age = 10

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

    @age.setter
    def age(self, value):
        if value < 0:
            value = 0
        self.__age = value

    @age.deleter
    def age(self):
        print("del age")
        del self.__age

# p = Person()
# # 访问
# print(p.age)
# # 赋值
# p.age = 19
# # 删除
# del p.age

help(Perosn)
# age 同样在 Data descriptors defined here: 区

>>>>> 打印结果

Help on class Person in module __main__:

class Person(builtins.object)
 |  Methods defined here:
 |  
 |  __init__(self)
 |      Initialize self.  See help(type(self)) for accurate signature.
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)
 |  
 |  age


Process finished with exit code 0

3.2 通过属性实例化方式 - 创建属性描述器

3.2.1 该实例的类必须要实现以下三个方法

__get__
__set__
__delete__
  • 优点
  • 上述3.1创建属性描述器方式,具有导致该类臃肿的弊端,试想:实现一个 age 属性描述器已经在 Person 类里面写了3个方法了,如果再多些属性,则会出现3*x 个方法。
  • 而通过属性实例化方式,则可以将对该属性进行操作描述的3个方法抽离到该实例类里面,更加面向对象了。
  • 这样的属性实例化方式也体现 python 语言一切皆对象的特性
# 属性描述器对象
class Age:
    def __get__(self, instance, owner):
        print("get")

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

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


# 类
class Person:
    age = Age() # 属性描述器实例化

# 此时 age 也是一个类属性,但之能通过类执行 get 方法,其他方法不会被转传到描述器中

# 属性操作
p = Person()
p.age = 10
print(p.age)
# del p.age

>>> 打印结果

set
get
None

3.2.2 通过属性实例化调用描述器时,使用注意事项:

  1. 使用实例进行调用
    最多三个方法都会被调用

  2. 使用类进行调用
    最多会调用get方法

  3. 不能顺利转换场景
    3.1 新式类和经典类
    描述器仅在新式类(继承自 object 的)中生效,且类及描述器类都是新式类

    3.2 方法拦截
    * 一个实例属性的正常访问顺序

    1 实例对象自身的__dict__字典
    2 对应类对象的__dict__字典
    3 如果有父类, 会再往上层的__dict__字典中检测
    4 如果没找到, 又定义了__getattr__方法, 就会调用这个方法
    
    • 而在上述的整个过程当中, 是如何将描述器的__get__方法给嵌入到查找机制当中?

    • 就是通过这个方法进行实现:__getattribute__

    • 内部实现模拟
      如果实现了描述器方法get就会直接调用
      如果没有, 则按照上面的机制去查找

4 描述器-和实例属性同名时, 操作优先级

  • 资料描述器:描述器类同时实现了 get set 方法

  • 非资料描述器:仅仅实现了 get 方法

  • 资料描述器 > 实例属性 > 非资料描述器

  • 1 资料描述器 > 实例属性 测试

class Age(object):
    def __get__(self, instance, owner):
        print("get")

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

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


class Person(object):
    age = Age()
    def __init__(self):
        self.age = 10

p = Person()

p.age = 10
print(p.age)

print(p.__dict__)

>>>> 打印结果
set
set
get
None
{}

  • 实例属性 > 非资料描述器 测试
class Age(object):
    def __get__(self, instance, owner):
        print("get")


class Person(object):
    age = Age()
    def __init__(self):
        self.age = 10

p = Person()

p.age = 10
print(p.age)


print(p.__dict__)

>>>> 打印结果

10
{'age': 10}

5 描述器-值的存储问题

  • 描述器Age是共享的
class Age:
    def __get__(self, instance, owner):
        print("get", self, instance, owner)
        if "v" in instance.__dict__:
            return instance.v

    def __set__(self, instance, value):
        print("set", self, instance, value)
        instance.v = value

    def __delete__(self, instance):
        print("delete", self, instance)
        del instance.v


class Person:
    age = Age()


p1 = Person()
print(p1.age)

p2 = Person()
print(p2.age)


>>>> 打印结果

get <__main__.Age object at 0x107e049e8> <__main__.Person object at 0x107e04a20> <class '__main__.Person'>
get <__main__.Age object at 0x107e049e8> <__main__.Person object at 0x107e04a58> <class '__main__.Person'>

# 只有 instance 的值是对应 Person 实例,而 Age 描述器则是同一个
  • 所以应该把值存放到对应的类实例中
class Age:
    def __get__(self, instance, owner):
        print("get", self, instance, owner)
        if "v" in instance.__dict__:
            return instance.v

    def __set__(self, instance, value):
        print("set", self, instance, value)
        instance.v = value

    def __delete__(self, instance):
        print("delete", self, instance)
        del instance.v


class Person:
    age = Age()


p1 = Person()
p1.age = 10
print(p1.age)

p2 = Person()
p2.age = 19
print(p2.age)

>>>> 打印结果

set <__main__.Age object at 0x10457b9e8> <__main__.Person object at 0x10457ba20> 10
get <__main__.Age object at 0x10457b9e8> <__main__.Person object at 0x10457ba20> <class '__main__.Person'>
10
set <__main__.Age object at 0x10457b9e8> <__main__.Person object at 0x10457ba58> 19
get <__main__.Age object at 0x10457b9e8> <__main__.Person object at 0x10457ba58> <class '__main__.Person'>
19

  • 有些场景也可以共享 描述器哦,如你想保存最新被修改的值时,就应该将值绑定到共享的描述器中 self.v = value,那么以后这个值就是一个共享值,哪个实例修改后,其他实例获取到的就是被新修改的值

问题

  1. 解析:描述器的定义方式为类属性形式,但我们操作使用为什么都是通过对象来调用?
  2. 类属性是被各个对象共享的,那么有如何保证不同的对象可以操作到这个共享属性的不同值?

相关文章

  • Python基础27-面向对象(系统内置方法7-描述器)

    7 描述器 1 概念 用于描述一个属性对应操作的对象。 属性对应操作一般为:增/改、删、查 2 作用 可以代为管理...

  • python基础-02

    Python 面向对象 python是一门面向对象的语言 Python内置类属性 python对象销毁(垃圾回收)...

  • Python面向对象 - 描述器

    描述器 什么是描述器?描述器是干什么用的? 现在有一个Person类来表示人。其中有两个属性,体重weight和身...

  • Python基础28-面向对象(系统内置方法8-装饰器)

    8 装饰器 1 装饰器(函数式实现) 2 通过类方式,实现装饰器 通常装饰器使用 使用@check 方式实际上就是...

  • python 面向对象

    python 面向对象 self代表类的实例,而非类 Python内置类属性 类方法 list、dict 等都成为...

  • python面向对象学习笔记-01

    学习笔记 # 0,OOP-Python面向对象 - Python的面向对象 - 面向对象编程 - 基础 -...

  • Python学习笔记5

    面向对象 类和对象的创建 属相相关 方法相关 元类 内置的特殊属性 内置的特殊方法 面向对象 类和对象的创建 类 ...

  • Python OOP-1

    0. OOP-Python面向对象 Python面向对象 面向对象编程基础公有私有继承组合,Mixin 魔法函数魔...

  • python 一图看懂 类的内置方法和属性

    内置方法 类似Java,Python中声明每一个类系统都会加上一些默认内置方法,提供给系统调用该类的对象时使用。比...

  • day-010--面向对象编程基础

    面向对象编程基础 面向对象技术简介类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每...

网友评论

      本文标题:Python基础27-面向对象(系统内置方法7-描述器)

      本文链接:https://www.haomeiwen.com/subject/ycxljftx.html