Python的functools模块完全教程

news2024/11/23 21:29:10

在python中函数是一等公民。Java中则为类是一等公民

当一个函数将另一个函数作为输入或返回另一个函数作为输出时,这些函数称为高阶函数

functools模块是Python的标准库的一部分,它是为高阶函数而实现的,用于增强函数功能

目录

一、@cache装饰器

二、@cached_property装饰器

三、@lru_cache装饰器

四、 partial函数

五、partialmethod函数

六、@singledispatch装饰器

七、@singledispatchmethod装饰器

八、update_wrapper与wraps函数


一、@cache装饰器

它的主要作用是提高函数的执行效率,特别是在函数被多次调用且输入参数相同的情况下,避免重复计算,如果一个函数在相同的输入参数下被多次调用,cache 会记住第一次调用的结果,并在后续调用中直接返回缓存的结果,而不是重新计算

它是线程安全的,因此可以在多个线程中使用。'

from functools import cache


def factorial(n):
    print("无cache执行")
    return n


@cache
def factorial_cache(n):
    print("执行cache")
    return n


if __name__ == '__main__':
    for i in range(10):
        factorial(10)
        factorial_cache(10)

二、@cached_property装饰器

用于将一个类的无参方法转换为缓存属性,意味着该方法在第一次调用时会被计算并缓存结果,后续调用时直接返回缓存的结果。缓存的结果是实例级别的,即每个实例都有自己的缓存。即使后续改变了方法内部涉及的类参数,输出也结果不会发生改变。

from functools import cached_property


class Circle:
    def __init__(self, radius):
        self.radius = radius

    @cached_property
    def area(self):
        # 如果类本身存在area属性,则此方法不生效。也就是本身属性读取和写入优先于 cached_property 方法
        print("计算面积")
        return 3.14159 * self.radius ** 2


if __name__ == '__main__':
    # 创建 Circle 对象
    circle = Circle(5)

    # 第一次访问 area 属性时会计算并缓存结果
    print(circle.radius, circle.area)
    del circle.radius
    circle.radius = 60
    # 第二次访问 area 属性时直接返回缓存的结果,即使是中途改变了圆的半径或者是删除了参与计算的参数。
    print(circle.radius, circle.area)

三、@lru_cache装饰器

lru_cache相较于cached_property,它装饰的方法是可以带参数的。

缓存是线程安全的,因此可以在 多个线程。

from functools import cached_property, lru_cache


class Circle:
    def __init__(self, radius):
        self.radius = radius

    @lru_cache(maxsize=128)  # 缓存最大容量
    def area(self, precision):
        # 如果类本身存在area属性,则此方法不生效。也就是本身属性读取和写入优先于 cached_property 方法
        print("计算面积")
        return 3.14159 * self.radius ** precision


if __name__ == '__main__':
    # 创建 Circle 对象
    circle = Circle(5)

    # 第一次访问会计算并缓存结果
    print(circle.radius, circle.area(precision=2))
    # 第二次访问如果输入参数相同,则直接返回缓存的结果,如果输入参数不同,则重新计算并缓存。
    print(circle.radius, circle.area(precision=3))
    print(circle.radius, circle.area(precision=3))
    print(circle.radius, circle.area(precision=2))

四、 partial函数

它允许你固定一个或多个参数,从而创建一个新的函数,这个新函数在调用时只需要提供剩余的参数。
 

from functools import partial


# 计算一个数的幂
def power(base, exponent):
    return base ** exponent


if __name__ == '__main__':
    # 但是我们需要计算是平方,只需要传入具体的数字。所以使用 partial 来简化,产生一个新的函数
    square = partial(power, exponent=2)

    print(square(3))  # 输出: 9
    print(square(4))  # 输出: 16

用于函数组合,即将多个函数组合成一个新的函数。

from functools import partial


# 加函数
def add(a, b):
    return a + b


# 积函数
def multiply(a, b):
    return a * b


if __name__ == '__main__':
    # 对函数进行组合,add函数的入参是固定的。我只需要它的结果作为乘积函数的入参。调用时只需要传入一个参数即可
    add_and_multiply = partial(multiply, add(2, 3))

    print(add_and_multiply(4))  # 输出: 20

简化回调函数

from functools import partial


def callback(a, b, c):
    print(f"a: {a}, b: {b}, c: {c}")


if __name__ == '__main__':
    # 使用 partial 固定 a 和 b 参数
    fixed_callback = partial(callback, a=1, b=2)

    fixed_callback(c=3)  # 输出: a: 1, b: 2, c: 3

五、partialmethod函数

它与 partial 类似,但更适合用于类方法,因为它会正确处理 self 参数。

from functools import partialmethod


class Demo:
    def __init__(self, value):
        self.value = value

    def get_something(self, name, age):
        return (self.value, name, age)

    # 使用 partialmethod 定义一个部分应用的方法
    get_something_with_default = partialmethod(get_something, name="长期", age=16)


if __name__ == '__main__':
    demo = Demo(value=1)
    print(demo.get_something(name=1, age=2))
    print(demo.get_something_with_default())

六、@singledispatch装饰器

它用于实现函数的多态性,即根据函数的第一个参数的类型来决定调用哪个具体的实现。singledispatch 允许你为一个函数定义多个实现,每个实现对应不同的参数类型。

一个非常有用的工具,特别是在你需要根据参数类型来选择不同实现的情况下。它使得代码更加清晰和可维护,避免了大量的条件判断。通过结合类型注解,singledispatch 可以进一步提高代码的可读性和可维护性。

from functools import singledispatch


# 定义一个通用的函数
@singledispatch
def process(arg):
    return f"Processing generic argument: {arg}"


# 为 int 类型定义一个特定的实现
@process.register
def _(arg: int):
    return f"Processing integer: {arg}"


# 为 str 类型定义一个特定的实现
@process.register(str)
def _(arg):
    return f"Processing string: {arg}"


# 为 list 类型定义一个特定的实现
@process.register
def _(arg: list):
    return f"Processing list: {arg}"


if __name__ == '__main__':
    # 调用不同类型的参数
    print(process(10))  # 输出: Processing integer: 10
    print(process("hello"))  # 输出: Processing string: hello
    print(process([1, 2, 3]))  # 输出: Processing list: [1, 2, 3]
    print(process(3.14))  # 输出: Processing generic argument: 3.14

七、@singledispatchmethod装饰器

它与 singledispatch 类似,但专门用于类方法(class methods)

from functools import singledispatchmethod


class Demo:
    @singledispatchmethod
    def process(self, arg):
        return f"Processing generic argument: {arg}"

    @process.register
    def _(self, arg: int):
        return f"Processing integer: {arg}"

    @process.register
    def _(self, arg: str):
        return f"Processing string: {arg}"

    @process.register
    def _(self, arg: list):
        return f"Processing list: {arg}"


if __name__ == '__main__':
    # 创建类的实例
    demo = Demo()

    # 调用不同类型的参数
    print(demo.process(10))  # 输出: Processing integer: 10
    print(demo.process("hello"))  # 输出: Processing string: hello
    print(demo.process([1, 2, 3]))  # 输出: Processing list: [1, 2, 3]
    print(demo.process(3.14))  # 输出: Processing generic argument: 3.14

八、update_wrapper与wraps函数

它用于更新一个包装函数(wrapper function)的元数据,使其看起来更像被包装的原始函数(wrapped function)。这在定义装饰器时非常有用,因为装饰器通常会返回一个新的函数,而这个新函数需要保留原始函数的元数据(如 __name__、__doc__、__module__ 等)

from functools import update_wrapper


def my_decorator(func):
    def wrapper(*args, **kwargs):
        print("Before function call")
        result = func(*args, **kwargs)
        print("After function call")
        return result

    # 使用 update_wrapper 更新 wrapper 的元数据
    update_wrapper(wrapper, func)
    return wrapper


@my_decorator
def my_function():
    """This is my function."""
    print("Inside my function")


# 调用被装饰的函数
my_function()

# 检查被装饰函数的元数据
print(my_function.__name__)  # 输出: my_function
print(my_function.__doc__)  # 输出: This is my function.

为了简化代码,functools 模块还提供了一个 wraps 装饰器,它实际上是 update_wrapper 的快捷方式。你可以使用 wraps 装饰器来代替手动调用 update_wrapper。

from functools import wraps


def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("Before function call")
        result = func(*args, **kwargs)
        print("After function call")
        return result

    return wrapper


@my_decorator
def my_function():
    """This is my function."""
    print("Inside my function")


# 调用被装饰的函数
my_function()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2203437.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

k8s部署及安装

1.1、Kubernetes 简介及部署方法 在部署应用程序的方式上面,主要经历了三个阶段 传统部署:互联网早期,会直接将应用程序部署在物理机上 优点:简单,不需要其它技术的参与 缺点:不能为应用程序定义资源使用边界,很难合理地分配计算…

量化交易四大邪术终章:春梦了无痕

做量化交易有些年头了,见过的策略也成百上千了,前段时间突发奇想,想揭露一些“照骗”策略,尽自己所能减少一些上当受骗的人数,于是写了一个量化邪术系列。 为什么叫量化交易邪术呢?因为在古早的简中网络中&…

netdata保姆级面板介绍

netdata保姆级面板介绍 基本介绍部署流程下载安装指令选择设置KSM为什么要启用 KSM?如何启用 KSM?验证 KSM 是否启用注意事项 检查端口启动状态 netdata和grafana的区别NetdataGrafananetdata各指标介绍总览system overview栏仪表盘1. CPU2. Load3. Disk…

TreeMap和TreeSet

前言 在了解TreeSet和TreeMap之前,先让我们介绍一下搜索树的概念。 1. 搜索树 二叉搜索树又称二叉排序树,这颗树要么是一棵空树,要么是具有以下性质的二叉树: 若它的左子树不为空,则左子树上所有节点的值都小于根节…

[Qt] 信号与槽:深入浅出跨UI与跨线程的信号发送

文章目录 如何自定义信号并使用自定义信号的步骤1.使用 signals 声明信号2. 信号的返回值是 void3. 在需要发送信号的地方使用 emit4. 使用 connect 链接信号和槽5. 完整代码示例总结 如何跨UI发送信号Qt跨UI发送信号机制详解案例概述Qt 信号与槽机制简介代码逻辑详解主窗口 Wi…

九APACHE

## 一 、HTTP协议与URL * HTTP协议:超文本传输协议,用于从Web服务器传输超文本到本地浏览器的传输协议,属于应用层协议。 超文本语言,用来创建超文本文件的标签 * URL:统一资源定位符,是互联网上标准资源…

centos 8.4学习小结

1.权限委派 2.vim快捷方式 2.1非正常关闭文本处理方式 2.2快捷方式 2.3TAB键补齐安装包 [ rootcloud Packages]# rpm -ivh bash-completion-2.7-5.el8.noarch.rpm 2.4#history 查询历史记录 [rootcloud ~]# vim /etc/profile HISTSIZE1000(默认保存1000条历史记…

基于SSM的老年人身心健康监管平台

文未可获取一份本项目的java源码和数据库参考。 选题意义 21世纪是全球人口老龄化的时代。联合国经济和社会事务部人口司发布的统计数据显示,截止到2018年7月,全球60岁及以上人口约为9.62亿,占总人口的比重约为12.8%。2018年底,我国60岁及以…

需求11——解决字段无法清空的两个小bug

目录 背景 第一个小bug——问题阐述 第一个小bug——解决方案 第二个小bug——问题阐述 第二个小bug——解决方案 总结 背景 已经写了一个上午的文章了,写完这篇就可以去吃饭了。这也是这几个月的我写的最后一个小bug文章,把这篇文章写完就搞定了…

vue 数组变化侦测

变更方法 Vue 能够侦听响应式数组的变更方法&#xff0c;并在它们被调用时触发相关的更新。这些变更方法包括: push() pop() shift() unshift() splice() sort() reverse() <template><div><p>点击按钮为列表添加元素</p><button click"cli…

【浏览器】如何正确使用Microsoft Edge

1、清理主页广告 如今的Microsoft Edge 浏览器 主页太乱了&#xff0c;各种广告推送&#xff0c;点右上角⚙️设置&#xff0c;把快速链接、网站导航、信息提要、背景等全部关闭。这样你就能得到一个超级清爽的主页。 网站导航       关闭 …

Mybatis高级查询-一对一查询

表介绍和表关系说明 新建以下4张表 tb_user&#xff1a;用户表 tb_order&#xff1a;订单表 tb_item&#xff1a;商品表 tb_orderdetail&#xff1a;订单详情表 【表关系】 1.tb_user和 tb_order表关系tb_user 《》 tb_order&#xff1a;一对多&#xff0c; 一个人可以下多…

第 4 章 Spring IoC容器之BeanFactory

Spring 的 IoC 容器是一个提供 IoC 支持的轻量级容器&#xff0c;除了基本的 IoC 支持&#xff0c;它作为轻量级容器还提供了 IoC 之外的支持。 Spring 提供了两种容器类型&#xff1a;BeanFactory 和 ApplicationContext&#xff1a; BeanFactory&#xff0c;基础类型 IoC 容…

布隆过滤器(Bloom Filter)详解

一、引言 在处理大量数据的场景中&#xff0c;我们经常会遇到判断一个元素是否在某个集合中的问题。传统的方法可能是使用 HashMap 等集合将数据保存起来&#xff0c;然后进行比较确定&#xff0c;但在元素很多的情况下&#xff0c;这种方式会非常浪费空间&#xff0c;检索速度…

dayu_widgets-简介

前言: 越来越多的人开始使用python来做GUI程序&#xff0c;市面上却很少有好的UI控件。即使有也是走的商业收费协议&#xff0c;不敢使用&#xff0c;一个不小心就收到法律传票。 一、原始开源项目: 偶然在GitHub上发现了这个博主的开源项目。https://github.com/phenom-films…

Fiddler配合wireshark解密ssl

环境&#xff1a; win11&#xff08;wireshark&#xff09;--虚拟机win7&#xff08;Fiddler&#xff09;---虚拟机win7&#xff08;HTTPS站点&#xff09; 软件安装问题&#xff1a; 需要.net环境&#xff0c;NDP461-KB3102436-x86-x64-AllOS-ENU.exe。 安装fiddler后安装下…

Pytest测试用例生命周期管理-Fixture

1、Fixture 用法 Fixture 特点及优势 1&#xff64;命令灵活&#xff1a;对于 setup,teardown,可以不起这两个名字2&#xff64;数据共享&#xff1a;在 conftest.py 配置⾥写⽅法可以实现数据共享&#xff0c;不需要 import 导⼊。可以跨⽂件共享3&#xff64;scope 的层次及…

从FastBEV来学习如何做PTQ以及量化

0. 简介 对于深度学习而言&#xff0c;通过模型加速来嵌入进C是非常有意义的&#xff0c;因为本身训练出来的pt文件其实效率比较低下&#xff0c;在讲完BEVDET后&#xff0c;这里我们将以CUDA-FastBEV作为例子&#xff0c;来向读者展示如何去跑CUDA版本的Fast-BEV&#xff0c;…

动态规划算法-路径问题——LCR.166.珠宝的最高价值

1.题目解析 题目来源&#xff1a;LCR.166珠宝的最高价值 原名&#xff1a;剑指offer47.礼物的最大价值 测试用例 2.算法原理 1.状态表示 创建dp表&#xff0c;dp[i][j]表示从最左上角到该目标位置的最大礼物值&#xff0c;代表dp[i][j]的状态 2.状态转移方程 目标位置的最大礼物…

SOMEIP_ETS_178: Subscribe_using_wrong_SOMEIP_MessageID

测试目的&#xff1a; 验证DUT能够拒绝一个SOME/IP头部使用错误消息ID进行服务发现的SubscribeEventgroup消息&#xff0c;并以SubscribeEventgroupNAck作为响应。 描述 本测试用例旨在确保DUT遵循SOME/IP协议&#xff0c;当接收到一个使用错误消息ID的服务发现SubscribeEve…