乔克视界 乔克视界
首页
  • 运维
  • 开发
  • 监控
  • 安全
  • 随笔
  • Docker
  • Golang
  • Python
  • AIOps
  • DevOps
  • 心情杂货
  • 读书笔记
  • 面试
  • 实用技巧
  • 博客搭建
友链
关于
收藏
  • 分类
  • 标签
  • 归档

乔克

云原生爱好者
首页
  • 运维
  • 开发
  • 监控
  • 安全
  • 随笔
  • Docker
  • Golang
  • Python
  • AIOps
  • DevOps
  • 心情杂货
  • 读书笔记
  • 面试
  • 实用技巧
  • 博客搭建
友链
关于
收藏
  • 分类
  • 标签
  • 归档
  • Docker

  • Golang

  • AIOps

  • Python

    • 基础知识

      • Python之链表
      • Python 之类的初识
      • Python之函数式编程
      • Python之匿名函数
      • Python之自定义函数
      • Python之异常处理
      • Python之条件与循环
      • Python之列表生成式
      • Python之生成器
      • Python之装饰器
        • 作用域
        • 高阶函数
        • 闭包
        • 装饰器
        • 装饰器类
      • Python之迭代器
      • Python之进程、线程、协程
      • Python之深浅拷贝
      • Python之反射
      • Python之并发编程
      • Python之垃圾回收机制
      • Python之断言assert
      • Python之上下文管理器和with语句
      • Python中JSON的对应关系
      • Python之单例模式
    • Django框架

    • 其他

  • DevOps

  • 专栏
  • Python
  • 基础知识
乔克
2025-07-19
目录

Python之装饰器

装饰器必备知识:

(1)、作用域

(2)、高阶函数

(3)、闭包

# 作用域

python 的作用域分四种:

(1)、L:Local,本地作用域

(2)、E:Enclosing,嵌套作用域

(3)、G:Global,全局作用域

(4)、B:Buit-in,系统内置作用域

从下面这个例子来具体了解这几个作用域分别在什么地方:

x = int(2.9)		 ## Buit-in
g_count = 0			 ## global
def outer():
  e_count = 0	   ## Enclosing
  def inner():
    l_count = 0  ## Local
1
2
3
4
5
6

如果嵌套作用域需要使用全局作用域的变量,需要申明 global,如下:

g_count = 0
def outer():
    global g_count
    print(g_count)
1
2
3
4

如果本地作用域需要使用嵌套作用域的变量,需要申明 nonlocal,如下:

def outer():
    e_count = 0
    def inner():
        nonlocal e_count
        print(e_count)
1
2
3
4
5

总结:

(1)、变量的查找顺序是:L-->E-->G-->B;

(2)、对于一个变量,局部要使用全局作用域,要申明 global;

(3)、对于一个变量,内部要使用外部作用域,要申明 nonlocal;

(4)、只有模块,类,函数才能引用作用域;

# 高阶函数

以下两种形式被称为高阶函数:

(1)、函数名作为函数参数;

(2)、函数名作为函数的返回值;

如下函数名作为函数参数:

>>> def func1(n):
...     return n*n
...
>>> def func2(a,b,f):
...     print(f(a)+f(b))
...
>>> func2(1,2,func1)
5
>>>
1
2
3
4
5
6
7
8
9

如下函数名作为返回值:

>>> def func1(n):
...     def func2(m):
...         print(n+m)
...     return func2
...
>>> f = func1(1)
>>> f(2)
3
>>>
1
2
3
4
5
6
7
8
9

# 闭包

如果在内部函数中,对外部作用域的变量进行了引用,那么内部函数就是闭包。

闭包=函数块+定义函数时的环境

如下例子:

>>> def outer():
...     n = 10
...     def inner():
...             print(n)
...     return inner
...
>>> f = outer()
>>> f
<function outer.<locals>.inner at 0x0000023AC9B33620>
>>> f()				## 这就是闭包
10
>>>
1
2
3
4
5
6
7
8
9
10
11
12

# 装饰器

装饰器的作用是修改其他函数的功能。

我们从一个例子来慢慢深入。

例子 1:领导叫你实现两个方法,分别是 foo 和 bar,如下:

import time
def foo():
    print("foo.....")
    time.sleep(2)

def bar():
    print("bar......")
    time.sleep(2)

foo()
bar()
1
2
3
4
5
6
7
8
9
10
11

功能已经实现了,用了两个普通的函数。

例子 2:有一天,领导叫你分别计算这两个函数执行所花费的时间,这一次你采用了高阶函数,如下:

import time
def showTime(f):
    start_time = time.time()
    f()
    end_time = time.time()
    print("Spend {} time".format(end_time - start_time))

showTime(foo)
showTime(bar)
1
2
3
4
5
6
7
8
9

到这一步我们已经实现了功能,但是我们改变了函数的调用方式。

例子 3:要在不改变函数调用方式的情况下实现功能。这一次我们要使用闭包,如下:

import time
def showTime(f):
    def inner():
        start_time = time.time()
    	f()
    	end_time = time.time()
    	print("Spend {} time".format(end_time - start_time))
    return inner

foo = showTime(foo)
foo()
bar = showTime(bae)
bar()
1
2
3
4
5
6
7
8
9
10
11
12
13

上面的 showTime()就是一个装饰器函数。

但是这种写法不够美观,为了能够更优雅,更美观,python 提供了装饰器的优雅写法,如下:

@showTime
def foo():
    print("foo.....")
    time.sleep(2)

@showTime
def bar():
    print("bar......")
    time.sleep(2)
1
2
3
4
5
6
7
8
9

上面我们已经知道装饰器的基本用法了,下面来介绍以下带参数的装饰器。

例子 4:使用装饰器实现一个加法器

import time
def showTime(f):
    def inner(x, y):
        start_time = time.time()
        f(x, y)
        end_time = time.time()
        print("Spend {} time".format(end_time - start_time))
    return inner

@showTime
def my_sum(a, b):
    print("a+b:{}".format(a+b))
    time.sleep(2)

my_sum(1, 2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

例子 5:带不定长参数的加法器

import time
def showTime(f):
    def inner(*args):
        start_time = time.time()
        f(*args)
        end_time = time.time()
        print("Spend {} time".format(end_time - start_time))
    return inner

@showTime
def my_sum(*args):
    sum = 0
    for i in args:
        sum += i
    print(sum)
    time.sleep(2)

my_sum(1, 2, 3, 4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

例子 6:给装饰器加参数。需求:给上面打印日志,根据需求打印日志,不是全部打印。

import time
def printLogs(flag):
    def showTime(f):
    	def inner(*args):
        	start_time = time.time()
        	f(*args)
        	end_time = time.time()
        	print("Spend {} time".format(end_time - start_time))
            if flag == 'true':
                print("日志记录")
    	return inner
    return showTime

@printLogs('True')			## 表示打印日志
def foo():
    print("foo.....")
    time.sleep(2)

@printLogs('Flase')			## 表示不打印日志
def bar():
    print("bar......")
    time.sleep(2)

foo()
foo.....
Spend 2.00173282623291 time
日志记录

bar()
bar......
Spend 2.0029125213623047 time

print(foo.__name__)
inner
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

我们看到最后这里,我们在 print(foo.name)的时候应该返回的是 foo,但是实际上返回的是 inner,这是由于装饰器重写了我们的函数名字和注释文档,这显然不是我们想要的。python 对此提供了一个简单的函数来解决这个问题:functools.wraps。

例子 7:我们把上面的函数改成下面的样子:

import time
from functools import wraps
def printLogs(flag):
    def showTime(f):
        @wraps(f)
        def inner(*args):
            start_time = time.time()
            f(*args)
            end_time = time.time()
            print("Spend {} time".format(end_time - start_time))
            if flag == 'true':
                print("日志记录")
        return inner
    return showTime

@printLogs('true')			## 表示打印日志
def foo():
    print("foo.....")
    time.sleep(2)

@printLogs('flase')			## 表示不打印日志
def bar():
    print("bar......")
    time.sleep(2)

print(foo.__name__)			## 现在输出是foo
print(bar.__name__)			## 现在输出是bar
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 装饰器类

类也可以用来构建装饰器。

如下例子:

import time
from functools import wraps
class printLogs(object):
    def __init__(self, flag):
        self.flag = flag

    def __call__(self, func):
        @wraps(func)
        def inner(*args):
            start_time = time.time()
            func(*args)
            end_time = time.time()
            print("Spend {} time".format(end_time - start_time))
            if self.flag == 'true':
                print("日志记录")
        return inner

@printLogs('true')			## 表示打印日志
def foo():
    print("foo.....")
    time.sleep(2)

@printLogs('flase')			## 表示不打印日志
def bar():
    print("bar......")
    time.sleep(2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

这样的话可扩展性就更高了,而且比嵌套函数更加直观。

作者:乔克

本文链接:https://jokerbai.com

版权声明:本博客所有文章除特别声明外,均采用 署名-非商业性-相同方式共享 4.0 国际 (CC-BY-NC-SA-4.0) 许可协议。转载请注明出处!

上次更新: 2025/07/19, 11:33:23
Python之生成器
Python之迭代器

← Python之生成器 Python之迭代器→

最近更新
01
使用 Generic Webhook Trigger 触发 Jenkins 多分支流水线自动化构建
07-19
02
使用Zadig从0到1实现持续交付平台
07-19
03
基于Jira的运维发布平台
07-19
更多文章>
Theme by Vdoing | Copyright © 2019-2025 乔克 | MIT License | 渝ICP备20002153号 |
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式