Python装饰器

装饰器简介

装饰器的作用: 在保证原来函数不变的情况下,直接给这个函数增加新的功能

#装饰器
'''
在不修改源代码的基础上,给函数增加新的功能
'''
#装饰器会将被装饰的函数当作参数传递给装饰器同名的函数
#添加装饰器,需要:
# 1.存在闭包
# 2.存在需要被装饰的函数

装饰器使用条件

#使用条件
'''
Python的装饰器,闭包是进入Python高级语法的基础,在使用
装饰器之前,有以下条件:
1.存在闭包
2.存在需要被装饰的函数
3.理解函数地址值的概念
'''

函数地址概念

当我们定义一个函数,使用print()方法打印其地址,每个地址唯一

#定义函数func
def func():
    print("你好")

print(func)  # <function func at 0x0000014C7AF31EA0>

闭包

#闭包
'''
闭包可以理解成"定义在一个函数内部的函数",
在一个函数中嵌套定义了一个函数,内函数里运用了外函数的临时变量,
并且外函数的返回值(return)是内涵上的引用(地址值),这样就构成了一个闭包
'''
#条件
# 1.存在函数的嵌套关系
# 2.内层函数引用了外层函数的变量
# 3.外城函数返回内层函数的地址值

def out_func(num):     #外部函数
    def in_func(in_num):  #内部函数
        print("函数的外部变量",num)  #打印外部函数传入的num
        print("函数的内部变量",in_num) #打印内部函数传入的in_num
    return in_func   #返回函数名存放的地址值

result = out_func(10)  #传入函数参数,返回in_func的地址,相当于result = in_func()
print(result)  #<function out_func.<locals>.in_func at 0x000002510F8437B8>
result(5)  #传入参数值5,调用in_func()

装饰器步骤

1.闭包

#==================闭包========
# 1.存在函数的嵌套关系
# 2.内层函数引用了外层函数的变量
# 3.外层函数返回内层函数的地址值

#外部函数为创建函数
def welcome(func):        #外部函数当作变量传入
    #内部函数是整理逻辑
    def in_func():
        print("欢迎光临")
        #调用被装饰的函数,实现两个函数体都被执行
        #且实现内部函数引用外部函数的变量
        func()  #调用外部函数
         #输出传入函数的地址值
        print("func的地址值",func)   #0x000001AB7F5237B8
        #输出内部函数的地址值
        print("in_func函数的地址值",in_func)  #0x000001AB7F523840
    return in_func #返回内部函数的地址

 

2.装饰器使用@闭包外函数的名称的形式

@welcome
def login():
    print("登陆成功")

装饰器下面的函数相当于传入装饰器同名函数的参数中

 

3.调用被装饰的函数

#调用被装饰的函数
login()
#输出login函数的地址值
print("login函数的地址值",login) #0x000001AB7F523840

 

从上述的输出地址过程中可以发现,被修饰的函数和闭包中的内部函数地址相同,说明是同一个函数的不同名;所有的功能和逻辑在闭包中,而被装饰的函数只是被闭包进行调用,在调用的过程中是闭包的内部函数自我调用的过程,因为内部函数中的函数是相同的函数

实战-计算器

import win32com.client



class Calc:
    #装饰器私有化,批量重复使用
    def __check_num_zsq(func):
        def inner(self,n):
            if not isinstance(n,int):
                raise TypeError("当前数据不是整型数值")
            return func(self,n)
        return inner
    #装饰器私有化,批量重复使用
    def __say_zsq(func):
        def inner(self,n):  
            #创建一个播报器对象
            speaker = win32com.client.Dispatch("SAPI.SpVoice")
            #通过这个播报器对象,直接播放相对应的语音字符就可以了
            speaker.Speak(n)
            return func(self,n) #函数返回值
        return inner
    

    @__check_num_zsq
    @__say_zsq
    def __init__(self,num):
        #使用私有实例对象属性,防止变量污染
        self.__result = num

    @__check_num_zsq
    @__say_zsq
    def jia(self,n):
        self.__result +=n

    @__check_num_zsq
    @__say_zsq
    def jian(self,n):
        self.__result -=n


    def multi(self,n):
        self.__result *=n
    
    def shows(self):
        print("计算的结果是:%d"%self.__result)

c1 = Calc(2)
c1.jia(3)
c1.jian(2)
c1.multi(5)
c1.shows()
计算器

上述代码中一个方法被多个装饰器装饰,并且想要语音播报根据不同的方法播报不同的内容,就需要指定不同类型的装饰器进行装饰,但是太冗余了,使用添加参数的装饰器更加简便

import win32com.client

class Calc:
    #装饰器私有化,批量重复使用
    def __check_num_zsq(func):
        def inner(self,n):
            if not isinstance(n,int):
                raise TypeError("当前数据不是整型数值")
            return func(self,n)
        return inner
    #装饰器私有化,批量重复使用
    def create_say_zsq(word=""):
        '''传入参数word'''
        def __say_zsq(func):
            #========此部分就是重写函数方法====
            def inner(self,n):  
                #创建一个播报器对象
                speaker = win32com.client.Dispatch("SAPI.SpVoice")
                #通过这个播报器对象,直接播放相对应的语音字符就可以了
                speaker.Speak(word+str(n))
                return func(self,n) #函数返回值
            return inner  #将函数返回给装饰器
            #========此部分就是重写函数方法====
        return __say_zsq #再把装饰器返回给带参数的装饰器

    @__check_num_zsq
    @create_say_zsq()
    def __init__(self,num):
        #使用私有实例对象属性,防止变量污染
        self.__result = num

    @__check_num_zsq
    @create_say_zsq("")
    def jia(self,n):
        self.__result +=n

    @__check_num_zsq
    @create_say_zsq("")
    def jian(self,n):
        self.__result -=n

    @__check_num_zsq
    @create_say_zsq("")
    def multi(self,n):
        self.__result *=n
    
    def shows(self):
        print("计算的结果是:%d"%self.__result)

c1 = Calc(2)
c1.jia(3)
c1.jian(2)
c1.multi(5)
c1.shows()
带参数的装饰器

 

优化代码,共用一个方法__say()使代码具有维护性

import win32com.client

class Calc:
    def __say(self,word):
        #创建一个播报器对象
        speaker = win32com.client.Dispatch("SAPI.SpVoice")
        #通过这个播报器对象,直接播放相对应的语音字符就可以了
        speaker.Speak(word)

    #装饰器私有化,批量重复使用
    def __check_num_zsq(func):
        def inner(self,n):
            if not isinstance(n,int):
                raise TypeError("当前数据不是整型数值")
            return func(self,n)
        return inner
    #装饰器私有化,批量重复使用
    def create_say_zsq(word=""):
        '''传入参数word'''
        def __say_zsq(func):
            #========此部分就是重写函数方法====
            def inner(self,n):  
                self.__say(word+str(n))
                return func(self,n) #函数返回值
            return inner  #将函数返回给装饰器
            #========此部分就是重写函数方法====
        return __say_zsq #再把装饰器返回给带参数的装饰器

    @__check_num_zsq
    @create_say_zsq()
    def __init__(self,num):
        #使用私有实例对象属性,防止变量污染
        self.__result = num

    @__check_num_zsq
    @create_say_zsq("")
    def jia(self,n):
        self.__result +=n

    @__check_num_zsq
    @create_say_zsq("")
    def jian(self,n):
        self.__result -=n

    @__check_num_zsq
    @create_say_zsq("")
    def multi(self,n):
        self.__result *=n
    
    def shows(self):
        self.__say("计算的结果是:%d"%self.__result)
        print("计算的结果是:%d"%self.__result)

c1 = Calc(2)
c1.jia(3)
c1.jian(2)
c1.multi(5)
c1.shows()
优化代码
import win32com.client

class Calc:
    def __say(self,word):
        #创建一个播报器对象
        speaker = win32com.client.Dispatch("SAPI.SpVoice")
        #通过这个播报器对象,直接播放相对应的语音字符就可以了
        speaker.Speak(word)

    #装饰器私有化,批量重复使用
    def __check_num_zsq(func):
        def inner(self,n):
            if not isinstance(n,int):
                raise TypeError("当前数据不是整型数值")
            return func(self,n)
        return inner
    #装饰器私有化,批量重复使用
    def create_say_zsq(word=""):
        '''传入参数word'''
        def __say_zsq(func):
            #========此部分就是重写函数方法====
            def inner(self,n):  
                self.__say(word+str(n))
                return func(self,n) #函数返回值
            return inner  #将函数返回给装饰器
            #========此部分就是重写函数方法====
        return __say_zsq #再把装饰器返回给带参数的装饰器

    @__check_num_zsq
    @create_say_zsq()
    def __init__(self,num):
        #使用私有实例对象属性,防止变量污染
        self.__result = num

    @__check_num_zsq
    @create_say_zsq("")
    def jia(self,n):
        self.__result +=n

    @__check_num_zsq
    @create_say_zsq("")
    def jian(self,n):
        self.__result -=n

    @__check_num_zsq
    @create_say_zsq("")
    def multi(self,n):
        self.__result *=n
    
    def shows(self):
        self.__say("计算的结果是:%d"%self.__result)
        print("计算的结果是:%d"%self.__result)
    @property #使用描述器访问方法与访问属性相同,并且无法修改私有属性
    def result(self):
        return self.__result
c1 = Calc(2)
c1.jia(3)
c1.jian(2)
c1.multi(5)
c1.shows()
print(c1.result)
使用描述器访问

使用链式编程的方法继续优化代码

import win32com.client

class Calc:
    def __say(self,word):
        #创建一个播报器对象
        speaker = win32com.client.Dispatch("SAPI.SpVoice")
        #通过这个播报器对象,直接播放相对应的语音字符就可以了
        speaker.Speak(word)

    #装饰器私有化,批量重复使用
    def __check_num_zsq(func):
        def inner(self,n):
            if not isinstance(n,int):
                raise TypeError("当前数据不是整型数值")
            return func(self,n)
        return inner
    #装饰器私有化,批量重复使用
    def create_say_zsq(word=""):
        '''传入参数word'''
        def __say_zsq(func):
            #========此部分就是重写函数方法====
            def inner(self,n):  
                self.__say(word+str(n))
                return func(self,n) #函数返回值
            return inner  #将函数返回给装饰器
            #========此部分就是重写函数方法====
        return __say_zsq #再把装饰器返回给带参数的装饰器

    @__check_num_zsq
    @create_say_zsq()
    def __init__(self,num):
        #使用私有实例对象属性,防止变量污染
        self.__result = num

    @__check_num_zsq
    @create_say_zsq("")
    def jia(self,n):
        self.__result +=n
        return self #返回对象本身

    @__check_num_zsq
    @create_say_zsq("")
    def jian(self,n):
        self.__result -=n
        return self #返回对象本身

    @__check_num_zsq
    @create_say_zsq("")
    def multi(self,n):
        self.__result *=n
        return self #返回对象本身
    def shows(self):
        self.__say("计算的结果是:%d"%self.__result)
        print("计算的结果是:%d"%self.__result)
        return self #返回对象本身
    @property #使用描述器访问方法与访问属性相同,并且无法修改私有属性
    def result(self):
        return self.__result
c1 = Calc(2)
c1.jia(3).jian(2).multi(5).shows()
链式编程

 

posted @ 2022-11-04 09:46  Crown-V  阅读(35)  评论(0)    收藏  举报