zoukankan      html  css  js  c++  java
  • python进阶04 装饰器、描述器、常用内置装饰器

    python进阶04 装饰器、描述器、常用内置装饰器

    一、装饰器

      作用:能够给现有的函数增加功能

      如何给一个现有的函数增加执行计数的功能

      首先用类来添加新功能

    def fun(): #首先我们定义一个函数
        print('func running')
    
    #看到封装,我们首先想到的是函数
    class MyFunc():
        def __init__(self,f): #把函数和变量封装在一起
            self.f=f
            self.count=0
        
        def run(self): #间接的调用了封装的函数
            self.count+=1
            return self.f()
    
    mf=MyFunc(func)#实例化
    mf.run()#输出func running 
    print(mf.count) #输出1
    mf.run()#输出func running 
    print(mf.count)#输出2
    mf.run()#输出func running 
    print(mf.count)#输出3
    #在python中,有一个__call__函数可以让这个对象可以调用
    class MyFunc():
        def __init__(self,f): #把函数和变量封装在一起
            self.f=f
            self.count=0
        
        def __call__(self): #让这个对象可以调用
            self.count+=1
            return self.f()
    
    mf =MyFunc(func)
    mf() #当你调用一个实例的时候,其实就是调用它的__call__方法,虽然mf()是一个实例,但也同时也是一个函数
    func() ——>func.__call__()
    
    mf() #func running
    print(mf.count)#1
    mf() #func running
    print(mf.count)#2
    mf() #func running
    print(mf.count)#3
    mf() #func running
    print(mf.count)#4
    def func():
        print('func running')
    
    class MyFunc():
        def __init__(self,f): #把函数和变量封装在一起
            self.f=f
            self.count=0
        
        def __call__(self,*arg,**kwarg): #为保证所有参数都能够传入,让这个对象可以调用
            self.count+=1
            return self.f(*arg,**kwarg)#调用封装进来的函数,并拿到他的返回值
    
    func = MyFunc(func) #这便是装饰,右边的实例由于有call方法,他便是一个函数右边的实例封装了原函数和计数器;然后又将这个实例赋值个func函数,让func不指向原函数,指向一个新的函数;看上去功能和原来一样,其实已经增加了功能
    #关于装饰,函数名还是原来的函数名,指向的却是新的函数,这个新的函数会调用原来的函数,也可以做些别的事情

     

     也可以用函数来封装

    #思路:装饰----一个新的函数,看上去功能和原来一样,其实已经增加了功能,增加了计数器
    
    #首先先定义一个函数:
    def my_func(f):
        return f()  #我在调用my_func的时候,其实已经调用了f()
    
    #然后就是添加功能,添加计数器
    def my_func(f):
        def new_func():
            new_func.count+=1 #计数器加1
            return f() #调用并返回f的内容
        new_func.count=0
        return new_func    
    
    func=my_func(func)#装饰
    #装饰,不过是指向了一个新的函数,只不过这个函数除了调用原来的函数以外,还会做点别的事
    
    def my_func(func):
        def new_func():
            new_func.count+=1#把计数器加1
            return func() #返回并调用原来的函数func
        new_func.count=0
        return new_func#只是返回,不调用
    
    @my_func #在定义的同时便装饰了
    def mimc(a,b):
      return a-b
    
    @MyFunc
    #在定义的同时便装饰了 def add(a,b): return a+b



    #总结:将新的功能和老的功能都绑定在一个新的函数里面

    二、描述器

      管理一个类属性的访问,修改,删减

    class MyAttribute:#将来他的实例会是一个类的属性(注意!是类的属性,不是实例的属性)
        def __get__(self,instance,owner):#instance是实例,owner是类
            print('get',instance,owner)
        def __set__(self,instance,value):#value是赋的值
            print('set',instance,value)
        def __delete__(self,instance,value):
            print('delete',instance)    
        def __str__(self):
            print('1111111')
    
    #重点来了
    class MyClass:
        attr=MyAttribute() #实例化
       
    mc=MyClass()
    print(mc.attr) #如果没有get set delete三种,就会输出字符串表示str的内容;如果够get,get就会被调用,就会替代字符串表示;set就是修改,delete就是删除
    #基于描述器的装饰器 Property
    class Property:
        def __init__(self,fget=None):
            self.fget=fget
    
        def __get__(self,obj,objtype=None):
            if obj is None:return selg
            if self.fget is None: raise AttributeError('无法访问这个属性')
        returnself.fget(obj)
    
    #这个东西不用理解
    #他是一个装饰器,也是一个描述器
    #例子:是否匿名
    class Person:
        def  __init__(self,name):
            self.name=name
            self.is_anonymous=False #是否是匿名
        
        @Property
        def get_name(self):#方法,所以若要拿到name的,肯定需要调用,如果不想调用,在上面加个@Property这个装饰器即可
            if not self.is_anonymous:
                return self.name
            else:
                return 'anonymous'
    
    p= Person('Tuple')
    p.is_anonymous=False
    print(p.get_name)#这样就可以直接取值,不需要print(p.get_name())

    三、常用内置装饰器

      property

    class Person:
        def __init__(self,name):
            self.name=name
    
        @property
        def name(self):
            print('通过property来获取name')
            if hasattr(self,'name'):
                return self.name
            else:
                raise AttributeError('没有name这个属性')
    
        @name.setter #控制赋值
        def name(self,value):
            print('通过property来设置name')
            self.name=value
    
        @name.deleter
        def name(self):
            print('通过property来删除name')
            del self.name
    
    #property 就是把一个方法变成一个属性的样子

      静态方法和类方法

    class A:
        @staticmethod #静态方法能够让类和实例,看他都是一个普通函数
        def meth():
            print('xxx')
    
    a=A()
    a.meth() #实例调用的时候——>A.meth(a),两者看起来都是函数
    
    class A:
        @classmethod #类方法:第一个参数,回传进去的都是类,不是实例
        def meth(cls): #cls表示类
            print('xxx')
    
    a=A()
    a.meth() #实例调用的时候——>A.meth(a)
    
    #正常情况下我们都应该传实例,但是以后做项目的时候会用到类方法
    #以后做数据库接入的时候,有一个ORM,在查询的时候,可以是由类方法,来简化你的代码

      四、关于一些内容的补充

      1、关于运行后进入交互环境的说明

    #IDLE执行以后,会进入shell模式(交互模式),此时程序还没有执行完,等待你输入下一段代码

      2、函数返回值的理解

    #区分func和func()
    #func是一个函数对象
    #func()分为两步:1、执行这个函数里面的内容。2、用return出来的返回值,替代这个调用
    def func():
        print('xxx')
        return 1
    
    print(func()) #相当于print(1)
    
    
    
    def other():#定义函数,是不会执行这个函数的
        print('other')
        return 10
    
    def func():
        print('xxx')
        return other()
    
    print(func())#输出 xxx other 10
    #文件--第一个执行的是func()
    #func--先打印出func
    #func--调用other
    #other--先打印出other
    #other--返回值10
    #func--在返回other()的值
    #文件--print要输出func()的值

      3、装饰后必须赋值变量

    def myfunc(func):
        def wrapper(*args,**kwargs):
            wrapper.count+=1
            return func(*args,**kwargs)
        wrapper.count=0
        return wrapper
    
    def func():
        print('xxx')
    
    f1=myfunc(func)
    f2=myfunc(func)
    print(f1 is f2) #两次装饰,都是得到了加强版的函数,但是两次装饰的函数时两个,因此他们拥有两个计数器,所以装饰完必须要有一个变量接收它,装饰以后必须赋变量

      4、装饰器

    #装饰,首先是加功能;碧玺,原来的是得做,还得加东西
    #原来的函数,功能一定要有;其次应该还有一个新的功能和她深度的绑定在一起
    
    #思考:我们装饰完以后,得到的必须是一个加强版的函数(新的函数)
    
    def func():
        print('func')
        return 1
    #如果我们要模仿这个函数,我们得实现什么
    #1、你需要打印出func 2、你也要返回出1
    
    #假如,我要写一个装饰器,这个装饰器什么功能都不加,只是单纯模仿
    def myfunc(f):
        return f
    
    f2=myfunc(func)#f2和func是一个函数
    #希望,我返回的不是原来的那个函数,而是新函数,但是做的事和原来一样
    
    def myfunc(f):
        def wrapper():#这个是定义,不是调用。定义完成以后会得到一个函数对象wrapper
            wrapper.count+=1#把我自己这个函数里面的count+1
            return f() #就是运行f,并且把它的返回值给返回出来(因为我要返回f(),就是返回f里面的return)
        wrapper.count=0 #由于是一个对象,所以可以加.count
        return wrapper#我返回的是wrapper这个函数对象(不是wrapper的调用)
    def myfunc(f)
        def wrapper():#这个封装的新函数:1、增加计数器 2、调用原函数,并返回其返回值
            wrapper.count+=1
            return f()
        wrapper.count=0
        return wrapper
    
    入门版:
    def myfunc(f):
        count=0
        def wrapper():
            nonlocal count #作用域的问题,全局变量和局部变量
            count+=1
            return f()
        return wrapper

      

  • 相关阅读:
    R语言:随机抽样(sample函数)
    SNP (Single Nucleotide Polymorphism), SNV ( single nucleotide variants ) , Indel (insertion-deletion) 的区别
    剑指offer五十六之删除链表中重复的结点
    剑指offer五十五之链表中环的入口结点
    剑指offer五十四之字符流中第一个不重复的字符
    剑指offer五十三之表示数值的字符串
    剑指offer五十二之正则表达式匹配
    剑指offer五十一之构建乘积数组
    求游戏晋级花费的宝石的期望
    剑指offer五十一之构建乘积数组
  • 原文地址:https://www.cnblogs.com/xuchengcheng1215/p/8469083.html
Copyright © 2011-2022 走看看