python装饰器简析


python装饰器

本篇尽可能的用简单易懂的方法解释装饰器的用法,所以不会出现高深的语言,同样,也可能不够严谨。

1 一些基本方法

1 函数的命名

def a():			
    print("Hello")
b = a				#将函数a()的地址赋值给b
b()					#输出b(),执行a地址
#输出:Hello
  • 当函数为有括号的形式时,如a(),b()代表一个函数本身
  • 当函数为没有括号形式时,如a,代表a()函数的地址

2 闭包

  • 定义一个函数中包含了另一个定义函数
def a():  # 定义一个函数a()
   x = 1
   y = 2

   def b():  # 在函数a()中定义一个函数b()
       print(x + y)  # 函数b()的内容

   return b  # 将函数b()的地址返回给函数a(),相当于a() = 函数b()的地址


a()()	# 这时a()代表b的地址,a()()=b(),怎么写可以执行但相当不规范,只为理解方便
#输出:3

将上面函数规范化

def a():  # 定义一个函数a()
    x = 1
    y = 2

    def b():  # 在函数a()中定义一个函数b()
        print(x + y)  # 函数b()的内容

    return b  # 将函数b()的地址返回给函数a(),相当于a() = 函数b()的地址


c = a()  # 此时将b的地址赋给c
c()  # 执行c(),也就是执行b()
# 输出:3

以规范化后的函数为例

函数的执行顺序为

a()			#执行a()
x=1			#将1赋予x
y=2			#将2赋予y
def b()		        #定义一个函数b()
return b	        #返回b地址,a()=b
c=b			#执行c=a(),因为a()=b,所以c=b
c()			#c()=b(),相当于执行b()
print(x+y)

2 装饰器

1 使用意义

  • 简单说就是在不改变函数本身的情况下,附加功能

  • #现有4份文件,任何人都可以查阅
    def screat0():
        print("机密0")
    def screat1():
        print("机密1")
    def screat2():
        print("机密2")
    def screat3():
        print("机密3")
    num = input("请输入编号")
    if num == "0":
        screat0()
    elif num == "1":
        screat1()
    elif num == "2":
        screat2()
    elif num == "3":
        screat3()
    
    #有一天老板觉得文件1,和文件2只能对特定的人开放
    #于是要求增加验证
    #必须在不改变现代码的情况下增加功能
    #这就是装饰器的意义
    #要用到上面闭包和给函数命名
    # 闭包****************************************************************************************
    databass = {'name': 'aot', 'password': '123'}
    def login(func):
        def inner():
            name = input("name:")
            password = input("password:")
            if name == databass['name'] and password == databass['password']:
                func()
            else:
                print("无访问权限")
    
        return inner
    #闭包*******************************************************************************************
    def screat0():
        print("机密0")
    def screat1():
        print("机密1")
    def screat2():
        print("机密2")
    def screat3():
        print("机密3")
    #-------------------------------------------------------------------------------------------------
    screat1 = login(screat1)	#将screat1地址传入login(func),函数调用不发生改变,见上文函数的命名
    screat2 = login(screat2)	#这两行代码可以用语法糖表示,见下文
    #------------------------------------------------------------------------------------------------
    num = input("请输入编号")
    if num == "0":
        screat0()
    elif num == "1":
        screat1()
    elif num == "2":
        screat2()
    elif num == "3":
        screat3()
    
    #语法糖
    screat1 = login(screat1) # 可以表示为@login放在对应的语句前,这句是放在 def screat1():前
    #如
    @login
    def screat1(): # 这时不需要再写 screat1 = login(screat1)
    #同理,也可以装饰screat0,1,2,3,只需在相应定义前增加即可,极大的方便了代码的修改
    
    #最终形态
    # 闭包****************************************************************************************
    databass = {'name': 'aot', 'password': '123'}
    def login(func):
        def inner():
            name = input("name:")
            password = input("password:")
            if name == databass['name'] and password == databass['password']:
                func()
            else:
                print("无访问权限")
    
        return inner
    #闭包*******************************************************************************************
    def screat0():
        print("机密0")
    @login
    def screat1():
        print("机密1")
    @login
    def screat2():
        print("机密2")
    def screat3():
        print("机密3")
    num = input("请输入编号")
    if num == "0":
        screat0()
    elif num == "1":
        screat1()
    elif num == "2":
        screat2()
    elif num == "3":
        screat3()
    
    
    • 如上,在仅仅增加了一段代码,并且不改变原有代码的情况下,便完成了验证这一附加功能
    • 一般情况下,代码不会只有这些,不改变原有代码增加功能是极其重要的

2 补充

修饰器是可以带参数的

如果原函数有参数,如

@login
def screat2(level):
    if(level<=5):
       print("机密2.1") 
    if(level>5):
        print("机密2.2")    

这时在调用原login(func)会报错,所以需要稍加修改

#原代码
def inner():
func()
#修改为
def inner(*args,**swargs):
func(*args,**swargs)
  • *args表示不定长传参,且不需要知道参数名称,以元组方式传入

  • **swargs也表示不定长传参,但需要知道参数名称,以字典方式传入

    #例
    def a(*a):
        print(a)
    a(1,2,12,3)
    #输出:(1, 2, 12, 3)
    
    def b(**b):
        print(b)
    b(a=1,b=2,c=3)
    #输出:{'a': 1, 'b': 2, 'c': 3}