装饰器

回顾闭包

  • 存在函数嵌套的前提下
  • 内部函数使用了外部函数的变量(入参或函数的局部变量)
  • 外部函数返回这个内部函数
def outer_fun(a):  # 外部函数
    def inner_fun(b):  # 内部函数
        return b + a  # 返回 b+a,a 属于外部函数
    return inner_fun  # 外部函数返回一个内部函数

fun = outer_fun(3)  # 是一个函数
result = fun(2)  # 调用这个函数(外部函数的入参 3 这里还能使用,要明白当前外部函数已经调用结束了,但是这里任能使用)
print(result)  # 输出 5

不修改原有函数代码的基础上扩展原有函数的功能,本质上是一个闭包

装饰器语法

def decorator(fn):  # 入参是一个方法
	def inner():
    """目标方法执行前"""
    fn()  # 执行目标方法
    """目标方法执行后"""
  return inner

@decorator  # 使用 decorator 方法增强当前方法
def target():
  pass

# 调用目标方法
target()

示例

def ext_say_hello(fun):
    def inner():
        print("张三说:", end="")
        fun()
    return inner

@ext_say_hello
def say_hello():
    print("Hello World")

say_hello()  # 张三说:Hello World

装饰器原理

原理如下,这个看起来稍微有点抽象,瞄一眼得了(也只是看起来抽象,其实不难理解)

# 满足闭包三要素:函数嵌套、内部函数使用外部函数变量(函数也可以作为变量)、外部函数返回内部函数
def ext_say_hello(fun):
    def inner():
        print("张三说:", end="")
        fun()
    return inner

# 要增强的方法
def say_hello():
    print("Hello World")

# 使用 ext_say_hello 增强 say_hello
# ext_say_hello 返回的是一个函数,使用 () 调用一下
# @ext_say_hello 等价于 ext_say_hello(say_hello)
ext_say_hello(say_hello)()  # 输出 张三说:Hello World

带参数的装饰器

三层函数嵌套

def who_to_zhangsan_say_hello(name):
    def zhangsan_say_hello(fun):
        def inner():
            print(f"{name}对张三说:", end="")
            fun()
        return inner
    return zhangsan_say_hello

@who_to_zhangsan_say_hello("李四")
def say_hello():
    print("Hello World")

say_hello()  # 李四对张三说:Hello World

@who_to_zhangsan_say_hello("李四") 分析,由 @who_to_zhangsan_say_hello("李四") 组成

  1. who_to_zhangsan_say_hello 方法是加了括号的,可以理解为调用了
  2. who_to_zhangsan_say_hello 方法的返回值是是 zhangsan_say_hello 方法
  3. 所以 who_to_zhangsan_say_hello("李四") 也就是 zhangsan_say_hello
  4. 所以 @who_to_zhangsan_say_hello("李四") 也就是 @zhangsan_say_hello

如果 目标函数有参数:

def who_to_zhangsan_say_hello(name):
    def zhangsan_say_hello(fun):
        def inner(*args, **kwargs):
            print(f"{name}对张三说:", end="")
            fun(*args, **kwargs)
        return inner
    return zhangsan_say_hello

@who_to_zhangsan_say_hello("李四")
def say_hello(words):
    print(words)

say_hello("hello world")  # 输出 李四对张三说:hello world

名称伪装

def who_to_zhangsan_say_hello():
    def zhangsan_say_hello(fun):
        def inner(*args, **kwargs):
            fun(*args, **kwargs)
        return inner
    return zhangsan_say_hello

@who_to_zhangsan_say_hello()
def say_hello(words):
    print(words)

print(say_hello.__name__)  # 输出 inner。因为这个函数已经被 inner 替换了,所以输出的是 inner 的名字

虽然能想通,但是如果能让名字保持原来的更易于可读性,这就是伪装

from functools import wraps

def who_to_zhangsan_say_hello():
    def zhangsan_say_hello(fun):

        @wraps(fun)  # 使用 wraps 伪装成目标函数名字
        def inner(*args, **kwargs):
            fun(*args, **kwargs)
        return inner
    return zhangsan_say_hello

@who_to_zhangsan_say_hello()
def say_hello(words):
    print(words)

print(say_hello.__name__)  # 输出 say_hello

属性装饰器

看一个传统的私有属性示例:

class MyClass:

    def __init__(self):
        self.__age = 0  # 双下划线,真私有属性,只能内部访问,所以提供一组 getter 和 setter

    def age_getter(self):
        return self.__age

    def age_setter(self, new_age):
        self.__age = new_age

if __name__ == '__main__':
    mc = MyClass()
    # print(mc.__age)  # 报错。没有这个属性,之前解释过了这是解释器修改了名称
    print(mc.age_getter())  # 输出 0
    mc.age_setter(1)  # 设置为 1
    print(mc.age_getter())  # 输出 1

属性装饰器就是把方法当做属性的方式来使用,方便一点(感觉上面的写法也不麻烦呀,可能 java 写多吧)

class MyClass:

    def __init__(self):
        self.__age = 0

    # getter。1,函数名称为 属性名称;2,加上 @property 修饰
    @property  
    def age(self):
        return self.__age

    # setter。1,函数名称为 属性名称;2,加上 @属性名.setter 修饰
    @age.setter  # setter,
    def age(self, new_age):
        self.__age = new_age

if __name__ == '__main__':
    mc = MyClass()
    print(mc.age)  # 输出 0。调用 age 的 getter
    mc.age = 1  # 输出 1。调用 age 的 setter
    print(mc.age)  # 输出 1
    # mc.age()  # 报错。因为 mc.age 表示调用 getter 已经回返回一个结果1。相当于 1() 
posted @ 2025-03-16 12:28  CyrusHuang  阅读(12)  评论(0)    收藏  举报