python(描述符应用与类的装饰器)

Posted hcy12

tags:

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

__enter__和__exit__

数据描述符:至少实现__get__,__set__方法的

非数据描述符:没有__set__方法的

上下文管理协议(with语句)改写,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法。

# -*- coding: utf-8 -*-
class Open:
    def __init__(self,name):
        self.name=name

    def __enter__(self):
        print(\'出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量\')
        # return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print(\'with中代码块执行完毕时执行我啊\')


with Open(\'b.txt\') as f:
    print(\'=====>执行代码块\')
    # print(f,f.name)
# -*- coding: utf-8 -*-
class Open:
    def __init__(self,name):
        self.name=name

    def __enter__(self):
        print(\'出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量\')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print(\'with中代码块执行完毕时执行我啊\')
        #下面三个ext执行完毕就代表with代码块里的语句执行完毕,出现异常,下面的代码不再执行
        print(exc_type) #异常类型
        print(exc_val)  #异常值
        print(exc_tb)   #追溯信息

with Open(\'b.txt\') as f:    #触发__enter__
    print(\'=====>执行代码块\')
    raise AttributeError(\'***着火啦,救火啊***\')
print(\'0\'*100)      #------->不会执行

用途:

1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预

2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制

描述符应用

控制输入类型

# -*- coding: utf-8 -*-
class Typed:
    def __init__(self,key):
        self.key=key

    def __get__(self,instance,owner):
        print(\'get方法\')
        # print(\'isinstance参数;\',isinstance)
        # print(\'owner参数;\',owner)
        return instance.__dict__[self.key]

    def __set__(self,instance,value):
        print(\'set方法\')
        # print(\'isinstance参数;\',instance)
        # print(\'owner参数;\',value)
        #通过初始化函数写活了
        if not isinstance(value,str):
            print(\'输入不是字符串\')
        else:
            instance.__dict__[self.key]=value

    def __delete__(self,instance):
            print(\'delete方法\')
            # print(\'isinstance参数;\', instance)
            instance.__dict__.pop(self.key)


class people:
    name=Typed(\'name\')
    # age=Typed(\'age\')
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary

p1=people("bob",22,5000)
p1.name
print(p1.__dict__)  #没有name属性,他被数据描述符代理了
del p1.name
print(p1.__dict__)
p2=people(1,22,5000)

进一步改进

# -*- coding: utf-8 -*-
class Typed:
    def __init__(self,key,expected_type):
        self.key=key
        self.expected_type=expected_type

    def __get__(self,instance,owner):
        # print(\'get方法\')
        # print(\'isinstance参数;\',isinstance)
        # print(\'owner参数;\',owner)
        return instance.__dict__[self.key]

    def __set__(self,instance,value):
        # print(\'set方法\')
        # print(\'isinstance参数;\',instance)
        # print(\'owner参数;\',value)
        #通过初始化函数写活了
        if not isinstance(value,self.expected_type):
            print(\'输入不是%s\'%self.expected_type)
        else:
            instance.__dict__[self.key]=value

    def __delete__(self,instance):
            print(\'delete方法\')
            # print(\'isinstance参数;\', instance)
            instance.__dict__.pop(self.key)


class people:
    name=Typed(\'name\',str)
    age=Typed(\'age\',int)
    salary=Typed(\'salary\',float)
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary

p1=people("bob",22,5000.0)
p1.name
print(p1.__dict__)  #没有name属性,他被数据描述符代理了
del p1.name
print(p1.__dict__)
p2=people(1,22,5000.0)

 类的装饰器

初级(没有参数):

# -*- coding: utf-8 -*-
def decorate(cls):
    print(\'类的装饰器开始运行啦------>\')
    return cls

@decorate       #无参:People=decorate(People)
class People:
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary
        print(self.age,self.name,self.salary)

p1=People(\'egon\',18,3333.3)    

------------恢复内容开始------------

__enter__和__exit__

数据描述符:至少实现__get__,__set__方法的

非数据描述符:没有__set__方法的

上下文管理协议(with语句)改写,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法。

# -*- coding: utf-8 -*-
class Open:
    def __init__(self,name):
        self.name=name

    def __enter__(self):
        print(\'出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量\')
        # return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print(\'with中代码块执行完毕时执行我啊\')


with Open(\'b.txt\') as f:
    print(\'=====>执行代码块\')
    # print(f,f.name)
# -*- coding: utf-8 -*-
class Open:
    def __init__(self,name):
        self.name=name

    def __enter__(self):
        print(\'出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量\')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print(\'with中代码块执行完毕时执行我啊\')
        #下面三个ext执行完毕就代表with代码块里的语句执行完毕,出现异常,下面的代码不再执行
        print(exc_type) #异常类型
        print(exc_val)  #异常值
        print(exc_tb)   #追溯信息

with Open(\'b.txt\') as f:    #触发__enter__
    print(\'=====>执行代码块\')
    raise AttributeError(\'***着火啦,救火啊***\')
print(\'0\'*100)      #------->不会执行

用途:

1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预

2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制

描述符应用

控制输入类型

# -*- coding: utf-8 -*-
class Typed:
    def __init__(self,key):
        self.key=key

    def __get__(self,instance,owner):
        print(\'get方法\')
        # print(\'isinstance参数;\',isinstance)
        # print(\'owner参数;\',owner)
        return instance.__dict__[self.key]

    def __set__(self,instance,value):
        print(\'set方法\')
        # print(\'isinstance参数;\',instance)
        # print(\'owner参数;\',value)
        #通过初始化函数写活了
        if not isinstance(value,str):
            print(\'输入不是字符串\')
        else:
            instance.__dict__[self.key]=value

    def __delete__(self,instance):
            print(\'delete方法\')
            # print(\'isinstance参数;\', instance)
            instance.__dict__.pop(self.key)


class people:
    name=Typed(\'name\')
    # age=Typed(\'age\')
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary

p1=people("bob",22,5000)
p1.name
print(p1.__dict__)  #没有name属性,他被数据描述符代理了
del p1.name
print(p1.__dict__)
p2=people(1,22,5000)

进一步改进

# -*- coding: utf-8 -*-
class Typed:
    def __init__(self,key,expected_type):
        self.key=key
        self.expected_type=expected_type

    def __get__(self,instance,owner):
        # print(\'get方法\')
        # print(\'isinstance参数;\',isinstance)
        # print(\'owner参数;\',owner)
        return instance.__dict__[self.key]

    def __set__(self,instance,value):
        # print(\'set方法\')
        # print(\'isinstance参数;\',instance)
        # print(\'owner参数;\',value)
        #通过初始化函数写活了
        if not isinstance(value,self.expected_type):
            print(\'输入不是%s\'%self.expected_type)
        else:
            instance.__dict__[self.key]=value

    def __delete__(self,instance):
            print(\'delete方法\')
            # print(\'isinstance参数;\', instance)
            instance.__dict__.pop(self.key)


class people:
    name=Typed(\'name\',str)
    age=Typed(\'age\',int)
    salary=Typed(\'salary\',float)
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary

p1=people("bob",22,5000.0)
p1.name
print(p1.__dict__)  #没有name属性,他被数据描述符代理了
del p1.name
print(p1.__dict__)
p2=people(1,22,5000.0)

 类的装饰器

初级(没有参数):

# -*- coding: utf-8 -*-
def decorate(cls):
    print(\'类的装饰器开始运行啦------>\')
    return cls

@decorate       #无参:People=decorate(People)
class People:
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary
        print(self.age,self.name,self.salary)

p1=People(\'egon\',18,3333.3)    

------------恢复内容开始------------

__enter__和__exit__

数据描述符:至少实现__get__,__set__方法的

非数据描述符:没有__set__方法的

上下文管理协议(with语句)改写,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法。

# -*- coding: utf-8 -*-
class Open:
    def __init__(self,name):
        self.name=name

    def __enter__(self):
        print(\'出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量\')
        # return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print(\'with中代码块执行完毕时执行我啊\')


with Open(\'b.txt\') as f:
    print(\'=====>执行代码块\')
    # print(f,f.name)
# -*- coding: utf-8 -*-
class Open:
    def __init__(self,name):
        self.name=name

    def __enter__(self):
        print(\'出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量\')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print(\'with中代码块执行完毕时执行我啊\')
        #下面三个ext执行完毕就代表with代码块里的语句执行完毕,出现异常,下面的代码不再执行
        print(exc_type) #异常类型
        print(exc_val)  #异常值
        print(exc_tb)   #追溯信息

with Open(\'b.txt\') as f:    #触发__enter__
    print(\'=====>执行代码块\')
    raise AttributeError(\'***着火啦,救火啊***\')
print(\'0\'*100)      #------->不会执行

用途:

1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预

2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制

描述符应用

控制输入类型

# -*- coding: utf-8 -*-
class Typed:
    def __init__(self,key):
        self.key=key

    def __get__(self,instance,owner):
        print(\'get方法\')
        # print(\'isinstance参数;\',isinstance)
        # print(\'owner参数;\',owner)
        return instance.__dict__[self.key]

    def __set__(self,instance,value):
        print(\'set方法\')
        # print(\'isinstance参数;\',instance)
        # print(\'owner参数;\',value)
        #通过初始化函数写活了
        if not isinstance(value,str):
            print(\'输入不是字符串\')
        else:
            instance.__dict__[self.key]=value

    def __delete__(self,instance):
            print(\'delete方法\')
            # print(\'isinstance参数;\', instance)
            instance.__dict__.pop(self.key)


class people:
    name=Typed(\'name\')
    # age=Typed(\'age\')
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary

p1=people("bob",22,5000)
p1.name
print(p1.__dict__)  #没有name属性,他被数据描述符代理了
del p1.name
print(p1.__dict__)
p2=people(1,22,5000)

进一步改进

# -*- coding: utf-8 -*-
class Typed:
    def __init__(self,key,expected_type):
        self.key=key
        self.expected_type=expected_type

    def __get__(self,instance,owner):
        # print(\'get方法\')
        # print(\'isinstance参数;\',isinstance)
        # print(\'owner参数;\',owner)
        return instance.__dict__[self.key]

    def __set__(self,instance,value):
        # print(\'set方法\')
        # print(\'isinstance参数;\',instance)
        # print(\'owner参数;\',value)
        #通过初始化函数写活了
        if not isinstance(value,self.expected_type):
            print(\'输入不是%s\'%self.expected_type)
        else:
            instance.__dict__[self.key]=value

    def __delete__(self,instance):
            print(\'delete方法\')
            # print(\'isinstance参数;\', instance)
            instance.__dict__.pop(self.key)


class people:
    name=Typed(\'name\',str)
    age=Typed(\'age\',int)
    salary=Typed(\'salary\',float)
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary

p1=people("bob",22,5000.0)
p1.name
print(p1.__dict__)  #没有name属性,他被数据描述符代理了
del p1.name
print(p1.__dict__)
p2=people(1,22,5000.0)

 类的装饰器

初级(没有参数):

# -*- coding: utf-8 -*-
def decorate(cls):
    print(\'类的装饰器开始运行啦------>\')
    return cls

@decorate       #无参:People=decorate(People)
class People:
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary
        print(self.age,self.name,self.salary)

p1=People(\'egon\',18,3333.3)    

进阶(有参数)

# -*- coding: utf-8 -*-
def typeassert(**kwargs):

    def decorate(cls):
        print(\'2------>\')
        for key,value in kwargs.items():
            setattr(cls,key,value)
        return cls
    print(\'1------>\', kwargs)
    return decorate
@typeassert()   #name=str,age=int,salary=float
#有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People)
class People:
    pass
    # def __init__(self,name,age,salary):
    #     self.name=name
    #     self.age=age
    #     self.salary=salary

print(People.__dict__)

@typeassert(hobby=\'ball\')
class bar():
    pass
#添加了hobby属性 print(bar.__dict__)

 可添加不定个数属性的示例:

# -*- coding: utf-8 -*-
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)  

自定制property

装饰器可以是函数,也可以是类类型。

# -*- coding: utf-8 -*-
class Lazyproperty:
    def __init__(self,func):
        self.func=func
    def __get__(self, instance, owner):
        print(\'这是我们自己定制的静态属性,r1.area实际是要执行r1.area()\')
        if instance is None:
            return self
        return self.func(instance) #此时你应该明白,到底是谁在为你做自动传递self的事情

class Room:
    def __init__(self,name,width,length):
        self.name=name
        self.width=width
        self.length=length

    @Lazyproperty #area=Lazyproperty(area) 相当于定义了一个类属性,即描述符
    def area(self):
        return self.width * self.length

r1=Room(\'alex\',1,1)
print(r1.area)      #将r1.area传参数写在Lazyproperty中的__get__里

 应用:自制property实现延迟计算功能

# -*- coding: utf-8 -*-
class Lazyproperty:
    def __init__(self,func):
        self.func=func
    def __get__(self, instance, owner):
        print(\'这是我们自己定制的静态属性,r1.area实际是要执行r1.area()\')
        if instance is None:
            return self
        else:
            print(\'--->\')
            value=self.func(instance)
            setattr(instance,self.func.__name__,value) #计算一次就缓存到实例的属性字典中
            return value
    # 如果加了set,将该非数据描述符变为数据描述符,优先级高于实列属性,缓存能力丧失
    # def __set__(self, instance, value):
    #     print(\'hahahahahah\')

class Room:
    def __init__(self,name,width,length):
        self.name=name
        self.width=width
        self.length=length

    @Lazyproperty #area=Lazyproperty(area) 相当于\'定义了一个类属性,即描述符\'
    def area(self):
        return self.width * self.length

r1=Room(\'alex\',1,1)
print(r1.area) #先从自己的属性字典找,没有再去类的中找,然后出发了area的__get__方法
print(r1.area) #先从自己的属性字典找,找到了,是上次计算的结果,这样就不用每执行一次都去计算

property补充:一个静态属性property本质就是实现了get,set,delete三种方法

# -*- coding: utf-8 -*-
class Foo:
    @property
    def AAA(self):
        print(\'get的时候运行我啊\')

    @AAA.setter
    def AAA(self,value):
        print(\'set的时候运行我啊\')

    @AAA.deleter
    def AAA(self):
        print(\'delete的时候运行我啊\')

#只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter
f1=Foo()
f1.AAA
f1.AAA=\'aaa\'
del f1.AAA

应用(类型检测)

# -*- coding: utf-8 -*-
class People:
    def __init__(self,name):
        self.name=name #实例化就触发property

    @property
    def name(self):
        # return self.name #无限递归
        print(\'get------>\')
        return self.DouNiWan

    @name.setter
    def name(self,value):
        print(\'set------>\')
        if not isinstance(value,str):
            raise TypeError(\'必须是字符串类型\')
        self.DouNiWan=value

    @name.deleter
    def name(self):
        print(\'delete------>\')
        del self.DouNiWan

p1=People(\'alex\') #self.name实际是存放到self.DouNiWan里
print(p1.name)

print(p1.__dict__)
p1.name=1

 元类

所有定义的类都是由type产生的。

# -*- coding: utf-8 -*-
class foo:
    pass
print(type(foo))

定义类的两种方法

# -*- coding: utf-8 -*-
class foo:
    pass
print(type(foo))
print(foo)

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

def test(self):
    print(\'=======>\')
Foo=type(\'foo1\',(object,),{\'x\':1,\'__init__\':__init__,\'test\':test})
print(Foo)
print(Foo.__dict__)
foo_1=Foo(\'alex\',22)
foo_1.test()

自定义元类(详情查看连接)

 

以上是关于python(描述符应用与类的装饰器)的主要内容,如果未能解决你的问题,请参考以下文章

理解Python装饰器

Python之旅的第28天(描述符类的装饰器)

Python 元类与类装饰器

Python学习之旅---描述符+装饰器应用

打字稿装饰器与类继承

装饰器、装饰器类与类装饰器(三)