描述符

Posted yuliangkaiyue

tags:

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

目录

描述符

什么是描述符

描述符的作用

描述符初探

描述符实战

雏形

进阶1

进阶2 添加类型限制

逐一限制

终极boss 类的装饰器

 

 

描述符

什么是描述符

描述符本质是一个类,在设计上至少实现了__set__() __get__() __delete__() 其中之一,这也被称为描述符协议。

 

__set__():为一个属性赋值时触发

__get__():获取一个属性时触发

__delete__():用del删除一个属性时触发

描述符的作用

把描述符定义成另一个类的属性,前提是不能定义到构造函数中,此时描述符就可以代理另一个类的属性。所以注意了,由描述符本身实例化的对象进行获取、赋值、删除操作并不会触发这三个方法。

描述符初探

 

 

技术分享图片
#描述符Str

class Str:

    def __get__(self, instance, owner):

      print(Str调用)

    def __set__(self, instance, value):

          print(Str设置...)

    def __delete__(self, instance):

         print(Str删除...)

 

#描述符Int

class Int:

    def __get__(self, instance, owner):

        print(Int调用)

    def __set__(self, instance, value):

        print(Int设置...)

    def __delete__(self, instance):

        print(Int删除...)

 

class People:

    name=Str()

    age=Int()

    def __init__(self,name,age): #name被Str类代理,age被Int类代理,

        self.name=name

        self.age=age

 

 

p1=People(alex,18)

 

#输出结果

Str设置...

Int设置...

 

#描述符Str的使用

p1.name

p1.name=egon

del p1.name

 

#输出结果

Str调用

Str设置...

Str删除..

#描述符Int的使用

p1.age

p1.age=18

del p1.age

 

#输出结果

Int调用

Int设置...

Int删除...

 

 

print(p1.__dict__)

print(People.__dict__)

 

# 输出结果可能让你费解

{}

{__module__: __main__, name: <__main__.Str object at 0x00000033945DEA58>, age: <__main__.Int object at 0x00000033945DE2B0>, __init__: <function People.__init__ at 0x0000003394753950>, __dict__: <attribute __dict__ of People objects>, __weakref__: <attribute __weakref__ of People objects>, __doc__: None}

#补充,看一下类型是否一致

print(type(p1) == People)

print(type(p1).__dict__ == People.__dict__)

 

True

True    
View Code

 

从初始化到一系列的调用、赋值、删除,我们都不难理解触发,但是在输出__dict__的时候,我们发现实例化的对象内容为空,类对象的内容输出就很明确的看到了其中的两个属性是类对象。所以我们可以自己理解为被代理的类自身只知道有什么属性,但是是什么样子的不清楚了,在代理那里保存着呢!

描述符也有自身要遵守的优先级问题:

  1. 类属性
  2. 数据描述符(至少实现了__get__()和__set__()
  3. 实例属性
  4. 非数据描述符(没有实现__set__()
  5. 找不到的属性触发__getattr__()

在此,不对优先级再做验证,出现问题是可以考虑是不是这方面原因。

描述符实战

实现用描述符实现类型控制

雏形

技术分享图片
class Str:
    def __init__(self, name):
        self.name = name

    def __get__(self, instance, owner):
        print(get--->, instance, owner)
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print(set--->, instance, value)
        instance.__dict__[self.name] = value

    def __delete__(self, instance):
        print(delete--->, instance)
        instance.__dict__.pop(self.name)


class People:
    name = Str(name)

    def __init__(self, name, age, salary):
        self.name = name
        self.age = age
        self.salary = salary


p1 = People(egon, 18, 3231.3)

# 调用
print(p1.__dict__)
p1.name
print(p1.name)

# 赋值
print(p1.__dict__)
p1.name = egonlin
print(p1.__dict__)

# 删除
print(p1.__dict__)
del p1.name
print(p1.__dict__)

set---> <__main__.People object at 0x000000D9C28CFC18> egon
{name: egon, age: 18, salary: 3231.3}
get---> <__main__.People object at 0x000000D9C28CFC18> <class __main__.People>
get---> <__main__.People object at 0x000000D9C28CFC18> <class __main__.People>
egon
{name: egon, age: 18, salary: 3231.3}
set---> <__main__.People object at 0x000000D9C28CFC18> egonlin
{name: egonlin, age: 18, salary: 3231.3}
{name: egonlin, age: 18, salary: 3231.3}
delete---> <__main__.People object at 0x000000D9C28CFC18>
{age: 18, salary: 3231.3}
View Code

 

 

进阶1

如果我在刚才代码里用类名调用会报错,说instance是空,所以需要修改get方法:

 

技术分享图片
def __get__(self, instance, owner):
    print(get--->,instance,owner)
    if instance is None:
        return self
    return instance.__dict__[self.name]
View Code

 

 

 

 

 

再次调用的时候就不会再报错了 输出:get---> None <class ‘__main__.People‘>

进阶2 添加类型限制

技术分享图片
class Str:
    def __init__(self,name,expected_type):
        self.name=name
        self.expected_type=expected_type
    def __get__(self, instance, owner):
        print(get--->,instance,owner)
        if instance is None:
            return self
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print(set--->,instance,value)
        if not isinstance(value,self.expected_type): #如果不是期望的类型,则抛出异常
            raise TypeError(Expected %s %str(self.expected_type))
        instance.__dict__[self.name]=value
    def __delete__(self, instance):
        print(delete--->,instance)
        instance.__dict__.pop(self.name)


class People:
    name=Str(name,str) #新增类型限制str
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary

p1=People(123,18,3333.3)#传入的name因不是字符串类型而抛出异常


set---> <__main__.People object at 0x00000053639A4438> 123
Traceback (most recent call last):
  File "D:/pycharm/model3/面向对象/描述符/磨刀霍霍.py", line 28, in <module>
    p1=People(123,18,3333.3)#传入的name因不是字符串类型而抛出异常
  File "D:/pycharm/model3/面向对象/描述符/磨刀霍霍.py", line 24, in __init__
    self.name=name
  File "D:/pycharm/model3/面向对象/描述符/磨刀霍霍.py", line 14, in __set__
    raise TypeError(Expected %s %str(self.expected_type))
TypeError: Expected <class str>
View Code

 

 

逐一限制

 
技术分享图片
class Typed:
    def __init__(self,name,expected_type):
        self.name=name
        self.expected_type=expected_type
    def __get__(self, instance, owner):
        print(get--->,instance,owner)
        if instance is None:
            return self
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print(set--->,instance,value)
        if not isinstance(value,self.expected_type):
            raise TypeError(Expected %s %str(self.expected_type))
        instance.__dict__[self.name]=value
    def __delete__(self, instance):
        print(delete--->,instance)
        instance.__dict__.pop(self.name)


class People:
    name=Typed(name,str)
    age=Typed(name,int)
    salary=Typed(name,float)
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary
View Code

 

但是如果属性很多,是不是这样子就有点麻烦

终极boss 类的装饰器

 

 

技术分享图片
class Typed:
    def __init__(self,name,expected_type):
        self.name=name
        self.expected_type=expected_type
    def __get__(self, instance, owner):
        print(get--->,instance,owner)
        if instance is None:
            return self
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print(set--->,instance,value)
        if not isinstance(value,self.expected_type):
            raise TypeError(Expected %s %str(self.expected_type))
        instance.__dict__[self.name]=value
    def __delete__(self, instance):
        print(delete--->,instance)
        instance.__dict__.pop(self.name)

def typeassert(**kwargs):
    def decorate(cls):
        print(类的装饰器开始运行啦------>,kwargs)
        for name,expected_type in kwargs.items():
            setattr(cls,name,Typed(name,expected_type))
        return cls
    return decorate
@typeassert(name=str,age=int,salary=float) #有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People)
class People:
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary

print(People.__dict__)
p1=People(egon,18,3333.3)


类的装饰器开始运行啦------> {name: <class str>, age: <class int>, salary: <class float>}
{__module__: __main__, __init__: <function People.__init__ at 0x000000BCA4A43950>, __dict__: <attribute __dict__ of People objects>, __weakref__: <attribute __weakref__ of People objects>, __doc__: None, name: <__main__.Typed object at 0x000000BCA48CEBA8>, age: <__main__.Typed object at 0x000000BCA4A3FC18>, salary: <__main__.Typed object at 0x000000BCA4A3FC88>}
set---> <__main__.People object at 0x000000BCA4A3FCF8> egon
set---> <__main__.People object at 0x000000BCA4A3FCF8> 18
set---> <__main__.People object at 0x000000BCA4A3FCF8> 3333.3
View Code

 

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

编写一个程序, 将 a.txt 文件中的单词与 b.txt 文件中的 单词交替合并到 c.txt 文件中, a.txt 文件中的单词用回车符 分隔, b.txt 文件中用回车或空格进行分隔。(代码片段

Java字符代码中干掉制表符回车符和换行符

通过 Java 正则表达式提取 semver 版本字符串的片段

android - 调整片段大小

Java方法

Java初识方法