当前位置: 首页 > news >正文

Python一分钟:装饰器

一、装饰器基础

函数即对象

在python中函数可以作为参数传递,和任何其它对象一样如:str、int、float、list等等

def say_hello(name):return f"Hello {name}"def be_awesome(name):return f"Yo {name}, together we're the awesomest!"def greet_bob(greeter_func):return greeter_func("Bob")

内部函数

在函数内部可以创建函数,这样的函数被称为内部函数:

def parent():print("Printing from parent()")def first_child():print("Printing from first_child()")def second_child():print("Printing from second_child()")second_child()first_child()

在python在交互式shell中运行:

python -i test.py

在使用 Python 时,尤其是在交互式 shell 中,其强大的自省(introspection)能力提供了极大的便利。自省是指对象在运行时能够了解自身属性的能力。

>>> parent()

内部函数只有在父函数被调用时才会被定义,它的作用域仅限于父函数parent()内:

>>> first_child()
Traceback (most recent call last):...
NameError: name 'first_child' is not defined

函数作为返回值

函数可以作为返回值返回:

def parent(num):def first_child():return "Hi, I'm Elias"def second_child():return "Call me Ester"if num == 1:return first_childelse:return second_child

通过变量保存返回的函数引用,可以像普通函数那样正常调用:

>>> first = parent(1)
>>> second = parent(2)>>> first
<function parent.<locals>.first_child at 0x7f599f1e2e18>>>> second
<function parent.<locals>.second_child at 0x7f599dad5268>

简单的装饰器

def decorator(func):def wrapper():print("Something is happening before the function is called.")func()print("Something is happening after the function is called.")return wrapperdef say_whee():print("Whee!")say_whee = decorator(say_whee)

在shell中运行:

>>> from hello_decorator import say_whee>>> say_whee()
# <function decorator.<locals>.wrapper at 0x7f3c5dfd42f0>

通过@符号来使用装饰器:

def decorator(func):def wrapper():print("Something is happening before the function is called.")func()print("Something is happening after the function is called.")return wrapper@decorator
def say_whee():print("Whee!")

传递参数:

def do_twice(func):def wrapper_do_twice(*args, **kwargs):func(*args, **kwargs)func(*args, **kwargs)return wrapper_do_twice

在shell中运行:

>>> from decorators import do_twice>>> @do_twice
... def return_greeting(name):
...     print("Creating greeting")
...     return f"Hi {name}"
...>>> return_greeting("Adam")
# Creating greeting
# Creating greeting
# 'Hi Adam'

保留原始信息

当函数被我们之前创建的装饰器装饰时,它的内部信息就会变得混乱,使用@functools.wraps装饰器可以解决这个问题:

import functoolsdef do_twice(func):@functools.wraps(func)def wrapper_do_twice(*args, **kwargs):func(*args, **kwargs)return func(*args, **kwargs)return wrapper_do_twice

在shell中运行:

>>> from decorators import do_twice>>> @do_twice
... def say_whee():
...     print("Whee!")
...>>> say_whee
<function say_whee at 0x7ff79a60f2f0>>>> say_whee.__name__
'say_whee'>>> help(say_whee)
Help on function say_whee in module whee:say_whee()

可以看到经过@functools.wraps装饰器后,say_whee函数的元数据被保留下来,包括函数名、文档字符串等。

二、更复杂的装饰器

嵌套装饰器

装饰器可以堆叠用于一个函数:

>>> from decorators import debug, do_twice>>> @debug
... @do_twice
... def greet(name):
...     print(f"Hello {name}")

带参数的装饰器

我们可以定义能够接收参数的装饰器,如下:

from decorators import repeat@repeat(num_times=4)
def greet(name):print(f"Hello {name}")greet("World")

实现方法如下:

import functools# ...def repeat(num_times):def decorator_repeat(func):@functools.wraps(func)def wrapper_repeat(*args, **kwargs):for _ in range(num_times):value = func(*args, **kwargs)return valuereturn wrapper_repeatreturn decorator_repeat

拆分讲解上面的代码:
首先decorator_repeat部分的行为,就和我们之前定义的普通装饰器一样:

...def decorator_repeat(func):@functools.wraps(func)def wrapper_repeat(*args, **kwargs):for _ in range(num_times):value = func(*args, **kwargs)return valuereturn wrapper_repeat
...

外层由repeat()函数接收参数,并返回wrapper_repeat装饰器的引用。

在装饰器中跟踪状态

可以实现装饰器对象中添加属性来追踪函数调用次数:

import functoolsdef count(func):@functools.wraps(func)def warp(*args, **kwargs):warp.num_calls += 1print(f"Call {warp.num_calls} of {func.__name__}()")return func(*args, **kwargs)warp.num_calls = 0return warp@count
def hello():print("Hello")hello()
hello()
hello()

类装饰器

有两种不同的方式可以在类中使用装饰器,第一种方法和我们上面学到的在函数中使用装饰器的方法非常类似。

Python有很多内置的装饰器很常用:

  • @classmethod
  • @staticmethod
  • @property

在python中维护状态的典型方法就是使用类。之前实现的count函数可以使用类装饰器重写。为了使实例可调用,需要实现.__call__()方法:

>>> class Counter:
...     def __init__(self, start=0):
...         self.count = start
...     def __call__(self):
...         self.count += 1
...         print(f"Current count is {self.count}")

每次调用实例都会执行.__call__()方法:

>>> counter = Counter()
>>> counter()
Current count is 1>>> counter()
Current count is 2>>> counter.count

实现装饰器类的典型方法是应该实现.__init__().__call__()

import functools# ...class CountCalls:def __init__(self, func):functools.update_wrapper(self, func)self.func = funcself.num_calls = 0def __call__(self, *args, **kwargs):self.num_calls += 1print(f"Call {self.num_calls} of {self.func.__name__}()")return self.func(*args, **kwargs)

使用方法:

>>> from decorators import CountCalls>>> @CountCalls
... def say_whee():
...     print("Whee!")
...>>> say_whee()
# Call 1 of say_whee()
# Whee!>>> say_whee()
# Call 2 of say_whee()
# Whee!>>> say_whee.num_calls
# 2

原文链接

http://www.lryc.cn/news/445304.html

相关文章:

  • Docker部署ddns-go教程(包含完整的配置过程)
  • 简单多状态dp第三弹 leetcode -买卖股票的最佳时机问题
  • 游戏化在电子课程中的作用:提高参与度和学习成果
  • php+mysql安装
  • 音视频入门基础:FLV专题(5)——FFmpeg源码中,判断某文件是否为FLV文件的实现
  • Tomcat 乱码问题彻底解决
  • RGB颜色模型
  • 智能工厂的软件设计 创新型原始制造商(“创新工厂“)的Creator原型(统一行为理论)之2
  • 【个人博客hexo版】hexo安装时会出现的一些问题
  • 道路裂缝,坑洼,病害数据集-包括无人机视角,摩托车视角,车辆视角覆盖道路
  • java接口文档配置
  • 【服务器第二期】mobaxterm软件下载及连接
  • 排序-----计数排序(非比较排序)
  • [Python]案例驱动最佳入门:Python数据可视化在气候研究中的应用
  • PyQt5 导入ui文件报错 AttributeError: type object ‘Qt‘ has no attribute
  • Unity中Rigidbody 刚体组件和Rigidbody类是什么?
  • MySQL学习笔记(持续更新中)
  • sqlserver插入数据删除数据
  • [51单片机] 简单介绍 (一)
  • 6个岗位抢1个人,百万年薪抢毕业生?大厂打响AI人才战
  • erlang学习:Linux命令学习3
  • 力扣41 缺失的第一个正数 Java版本
  • 第五篇:Linux进程的相关知识总结(1)
  • 企业级Windows server服务器技术(1)
  • 【30天玩转python】单元测试与调试
  • 13.第二阶段x86游戏实战2-动态模块地址
  • 【WebLogic】WebLogic 11g 控制台模式下安装记录
  • 失踪人口回归(明天开始继续更新学习内容)
  • AIGC对网络安全的影响
  • golang学习笔记25——golang 实现 MD5加密、RSA加密 和 Base64编码