小甲鱼 发表于 2022-1-7 05:12:09

已有 25 人购买  本主题需向作者支付 5 鱼币 才能浏览 购买主题

小古比鱼 发表于 2022-4-8 17:26:56

本节首先回顾了闭包的原理,而后通过两个函数作为参数传递给另一个函数来调用的例子引入了装饰器的概念——所谓装饰器,就是在不修改原来代码的前提下增加新功能,拿函数作为参数,调用装饰器函数,实则还是利用了闭包的特性。“@+装饰器”其实是一个语法糖,等价于“函数 = 装饰器(函数)”;前者写法简单,使用起来更方便!此外,多个装饰器可以用在同一个函数上,还可以通过函数的多层嵌套调用给装饰器传递参数。尽管看上去很复杂,背后的原理很简单。作为一名程序员,只要掌握了装饰器的精髓,就再也不用担心产品经理给我们出难题啦!

fishcyou 发表于 2022-5-2 19:01:40

装饰器可以实现在不修改原来代码的前提下增加新功能。

(通过将原来代码作为函数,传入装饰器,返回新的结果)

import time

def time_master(func):
    def call_func():
      print("开始运行程序...")
      start = time.time()
      func()
      stop= time.time()
      print("结束程序运行...")
      print(f"一共耗费了 {(stop-start):.2f} 秒.")
    return call_func

@time_master
def myfunc():
    time.sleep(2)
    print("Hello Python.")

myfunc()
>>>
开始运行程序...
Hello Python.
结束程序运行...
一共耗费了 2.13 秒.

此外,多个装饰器可以用在同一个函数上,还可以通过函数的多层嵌套调用给装饰器传递参数。

(多个@装饰器时,执行顺序是从下往上)

def square(func):
    def inner():
      x = func()
      return x ** 2
    return inner

def add(func):
    def inner():
      x = func()
      return x + 3
    return inner

@square
@add
def test():
    return 2

print(test()) # 顺序为(2+3)** 2
>>>
25

装饰器也可以传入参数。

import time

def logger(msg):
    def time_master(func):
      def inner():
            start = time.time()
            func()
            stop= time.time()
            print(f"[{msg}]一共耗费了 {(stop-start):.2f} 秒.")
      return inner
    return time_master

@logger(msg='A') # 等同于funA = logger(msg=‘A’)(funA)
def funA():
    print("正在运行程序funA...")
    time.sleep(1)

@logger(msg='B')
def funB():
    print("正在运行程序funB...")
    time.sleep(2)
   
funA()
funB()
>>>
正在运行程序funA...
一共耗费了 1.08 秒.
正在运行程序funB...
一共耗费了 2.03 秒.

8245k 发表于 2022-8-1 23:41:52

import time
   
def logger(msg):
    def time_master(func):
      def useless():
            
            def call_func():
                start = time.time()
                func()
                stop = time.time()
                print(f"[{msg}]一共耗费了 {(stop-start):.2f}")
            return call_func
      return useless()
    return time_master
   
@logger(msg="A")
def funA():
    time.sleep(1)
    print("正在调用funA...")
   
@logger(msg="B")
def funB():
    time.sleep(1)
    print("正在调用funB...")
   
funA()
funB()

第三个知识点让我好奇,这个语法糖是否可以不断向内挖掘嵌套函数,直到挖到最底层函数,然后将这个最底层的函数赋值给原函数?
这里在在logger函数中间添加了一个useless用来检测,经过实践确实是这样的,我随后还试了试两个useless,也是同样的结果
正在调用funA...
一共耗费了 1.01
正在调用funB...
一共耗费了 1.00

无言之梦 发表于 2022-9-1 15:45:42

装饰器第一部分的闭包过程中不打 return call_func为什么报错

炎凉来寻 发表于 2022-9-8 10:36:47

需要注意的是   ‘return 函数名’   的后面是否加‘()’
def cube(func):
    def inner():
      def a():
            def b():
                x = func()
                return x * x * x
            return b()
      return a()
    return inner
   
@cube
def test():
    return 2
   
print(test())

等价于
def cube(func):
    def inner():
      def a():
            def b():
                x = func()
                return x * x * x
            return b
      return a
    return inner
   
@cube
def test():
    return 2
   
print(test()()())

Free_num 发表于 2022-9-25 12:18:31


我觉得可以这么理解语法糖
import time

def time_master(func):
    def call_func():
      print('开始运行程序...')
      start = time.time()
      func()                     # 函数嵌套调用   func是形参,myfunc是实参因此这里会执行myfunc()函数
      stop = time.time()
      print('结束程序运行...')
      print(f'一共耗费了{(stop-start):.2f}秒')
    return call_func            # 嵌套函数的返回值 返回到>>>函数调用处

def myfunc():
    time.sleep(2)
    print('Python')
a = time_master(myfunc)   #这是函数调用处》》也就是说 a = time_master(myfunc)就是call_func
a()                                 # 所以这里就是 call_func()

Free_num 发表于 2022-9-25 12:48:24

8245k 发表于 2022-8-1 23:41
第三个知识点让我好奇,这个语法糖是否可以不断向内挖掘嵌套函数,直到挖到最底层函数,然后将这个最底层 ...

我认为是由于func()这个嵌套调用在最最内存函数call_func()中,所以最后实质上才是call_func()这个函数的实现

阿伟同学 发表于 2022-9-28 21:21:55

本帖最后由 阿伟同学 于 2022-9-28 21:23 编辑

8245k 发表于 2022-8-1 23:41
第三个知识点让我好奇,这个语法糖是否可以不断向内挖掘嵌套函数,直到挖到最底层函数,然后将这个最底层 ...

问题不在这,是您retrun useless() 加上括号了{:10_263:}按小甲鱼等价的
funA = logger(msg = "A")(funA)你这个相当于还是返回call_func的引用,你要是把括号去了就大不一样了

chenjinchao 发表于 2022-10-6 17:10:01

难成狗啊,我要多看几次才行,坚持把这吃透

foolwolf0068 发表于 2022-10-6 17:37:56

装饰器,确实很强大, 但是理解起来确实比较费力,继续加油

墨墨在努力吖 发表于 2022-10-14 15:23:46

滴滴滴~打卡{:10_298:}

七钱五 发表于 2022-10-16 16:47:58

测试函数不能带参数,不然会报错,这是为什么呢?比如只能funA(),而不能是funA(x){:10_269:}

CDra 发表于 2022-10-21 14:31:01

8245k 发表于 2022-8-1 23:41
第三个知识点让我好奇,这个语法糖是否可以不断向内挖掘嵌套函数,直到挖到最底层函数,然后将这个最底层 ...

不会把,装饰器只会将函数作为参数传一次,你一直往下嵌套直到最底层,是因为你返回函数的时候加了()

lymbwx 发表于 2022-10-25 10:37:03

语法糖很赞啊

lymbwx 发表于 2022-10-25 10:41:25

装饰器相对于语法糖,但是看起来更简洁直接

migu_sm1 发表于 2022-11-7 18:43:35

Learning...

Loafer0 发表于 2022-11-25 20:51:50

七钱五 发表于 2022-10-16 16:47
测试函数不能带参数,不然会报错,这是为什么呢?比如只能funA(),而不能是funA(x)

我也遇到了同样的问题,想知道后面怎么解决了?{:10_285:}

呱呱呱i 发表于 2022-12-30 22:32:16

{:10_266:}

andyleesh 发表于 2023-1-2 13:52:09

打卡
页: [1] 2
查看完整版本: 第046讲:函数(VI)- 装饰器