《Fluent Python》笔记 | 函数对象和装饰器

news2024/11/18 5:37:08

在Python中函数是对象,本质是function类的实例。同样函数对象也是“一等对象”,即满足以下条件:

  • 在运行时创建
  • 能赋值给变量或数据结构中的元素
  • 能作为参数传给函数
  • 能作为函数的返回结果

函数对象的__doc__属性用于生成对象的帮助文本。

接受函数为参数, 或者把函数作为结果返回的函数是高阶函数(higherorder function),例如map、filter、reduce等函数。

lambda关键字在 Python 表达式内创建匿名函数

可调用对象

可调用对象为可以应用调用运算符()的对象。

Python文档中列出以下7种可调用对象。

  • 用户定义的函数

    使用 def 语句或 lambda 表达式创建。

  • 内置函数

    使用 C 语言(CPython) 实现的函数, 如 len 或 time.strftime。

  • 内置方法

    使用 C 语言实现的方法, 如 dict.get。

  • 方法
    在类的定义体中定义的函数。


  • 调用类时会运行类的 __new__ 方法创建一个实例, 然后运行__init__ 方法, 初始化实例, 最后把实例返回给调用方。

  • 类的实例
    如果类中定义了 __call__方法, 那么它的实例可以作为函数调用。

  • 生成器函数
    使用 yield 关键字的函数或方法。 调用生成器函数返回的是生成器对象。

可以通过内置函数callable() 判断对象是否可调用。

函数属性

既然我们说函数是一个对象,那么它也会像对象一样拥有属性。下表列出了一些只有用户定义的函数对象具有而常规对象没有的属性。

名称类型说明
__annotations__dict参数和返回值的注解
__call__method wrapper实现 () 运算符; 即可调用对象协议
__closure__tuple函数闭包, 即自由变量的绑定(通常是 None)
__code__code编译成字节码的函数元数据和函数定义体
__defaults__tuple形式参数的默认值
__get__method wrapper实现只读描述符协议
__globals__dict函数所在模块中的全局变量
__kwdefaults__dict仅限关键字形式参数的默认值
__name__str函数名称
__qualname__str函数的限定名称, 如 Random.choice

仅限关键字参数

仅限关键字参数(keyword-only argument)。

def tag(name, *content, cls=None, **attrs): pass
def f(a, *, b): pass

tag函数的输入参数中,第一个参数后面的任意个参数会被 *content 捕获, 存入一个元组(即content为元组)。在tag函数定义中没有明确指定名称的关键字参数会被 **attrs 捕获, 存入一个字典(即attrs为字典)。 在上面代码中,name参数和a参数为定位参数,cls参数和b参数为仅限关键字参数。如果仅限关键字参数没有默认值,那么强制传入对应实参。

获取函数的信息

  • 获取函数参数信息

    函数对象有个 __defaults__ 属性, 它的值是一个元组, 里面保存着定位参数和关键字参数的默认值。 仅限关键字参数的默认值在__kwdefaults__ 属性中。 参数的名称在 __code__ 属性中, 它的值是一个 code 对象引用, 自身也有很多属性。

  • 获取函数注解

    带注解的函数声明示例:

    def clip(text:str, max_len:'int > 0'=80) -> str: pass
    

    各个参数在 : 之后添加注解表达式,若参数有默认值,则在参数名和=之间加入注解表达式。如果想注解返回值, 在 ) 和函数声明末尾的 : 之间添加 -> 和注解表达式。

    注解不会做任何处理, 只是存储在函数的 __annotations__ 属性(一个字典) 中。

    函数装饰器

    函数装饰器用于在源码中“标记”函数, 以某种方式增强函数的行为。 装饰器是可调用的对象, 其参数是另一个函数(被装饰的函数) 。 装饰器可能会处理被装饰的函数, 然后把它返回, 或者将其替换成另一个函数或可调用对象。

    装饰器替换被装饰函数示例:

    def deco(func):		# deco是一个装饰器,其输入参数时另一个函数
    	def inner():
    		print('running inner()')
    	return inner
    
    @deco
    def target():	# 使用 deco 装饰 target
    	print('running target()')
        
    >>> target()
    running inner()
    >>> target
    <function deco.<locals>.inner at 0x10063b598>
    

装饰器的第一大特性是, 能把被装饰的函数替换成其他函数。 第二个特性是, 装饰器在加载模块时立即执行

装饰器通常在一个模块中定义, 然后应用到其他模块中的函数上。

大多数装饰器会在内部定义一个函数, 然后将其返回,替换被装饰的函数。

闭包

在这里插入图片描述

>>> avg = make_averager()
>>> avg(10)
10.0
>>> avg(11)
10.5
>>> avg(12)
11.0

在上面的示例中,seriesmake_averager 函数的局部变量。 可是,调用 avg(10)时, make_averager 函数已经返回了,而它的本地作用域也一去不复返了。但代码测试结果告诉我们, series变量以某种形式保存了下来,这就是闭包的特性。series自由变量,即未在本地作用域中绑定的变量。

闭包是一种函数, 它会保留定义函数时存在的自由变量的绑定,这样调用函数时, 虽然定义作用域不可用了, 但是仍能使用那些绑定。

只有嵌套在其他函数中的函数才可能需要处理不在全局作用域中的外部变量,也就是说只有嵌套在其他函数中的函数才会涉及到闭包的知识。

nonlocal关键字能把变量标记为自由变量。示例如下:

def make_averager():
	count = 0
	total = 0
	def averager(new_value):
		nonlocal count, total	# count, total都为自由变量,都会在闭包中进行绑定和更新
		count += 1
		total += new_value
		return total / count
	return averager

实现一个简单的装饰器,用于计算被调用函数的运行时间。

import time
def clock(func):
	def clocked(*args):
		t0 = time.perf_counter()
        result = func(*args)
        elapsed = time.perf_counter() - t0
        name = func.__name__
        arg_str = ', '.join(repr(arg) for arg in args)
        print('[%0.8fs] %s(%s) -> %r' % (elapsed, name, arg_str, result))
        return result
	return clocked

上述代码反映了装饰器的典型行为: 把被装饰的函数替换成新函数, 二者接受相同的参数, 而且通常返回被装饰的函数本该返回的值, 同时还会做些额外操作。

标准库中的装饰器

  • functools.lru_cache

    functools.lru_cache是非常实用的装饰器, 实现了备忘功能。 这是一项优化技术, 它把耗时的函数的结果保存起来, 避免传入相同的参数时重复计算。 LRU 三个字母是“Least Recently Used”的缩写, 表明缓存不会无限制增长, 一段时间不用的缓存条目会被清除。

    该装饰器可以使用两个可选参数进行配置。

    functools.lru_cache(maxsize=128, typed=False)  
    
    • maxsize:指定存储多少个调用的结果。
    • typed:若为 True, 把不同参数类型得到的结果分开保存,即把通常认为相等的浮点数和整数参数(如 1 和 1.0) 区分开。
  • functools.singledispatch

    functools.singledispatch 装饰器可以把整体方案拆分成多个模块, 甚至可以为无法修改的类提供专门的函数(使用@<base_function>.register(<type>) 装饰)。 使用@singledispatch 装饰的普通函数会变成泛函数(generic function) :根据第一个参数的类型, 以不同方式执行相同操作的一组函数。

叠放装饰器

因为装饰器是一个函数(可调用对象),所以可以组合使用。

@d1
@d2
def f():
	print('f')

等同于:

def f():
	print('f')
f = d1(d2(f))  

参数化装饰器

为了实现参数化装饰器,我们引入装饰器工厂函数,装饰器函数嵌套在该函数中。调用装饰器工厂函数会返回真正的装饰器, 这才是应用到目标函数上的装饰器。参数化装饰器示例如下:

import time

DEFAULT_FMT = '[{elapsed:0.8f}s] {name}({args}) -> {result}'

def clock(fmt=DEFAULT_FMT): 	# clock是参数化装饰器工厂函数
	def decorate(func): 		# decorate 是真正的装饰器
		def clocked(*_args): 	# clocked 包装被装饰的函数
            t0 = time.time()	
            _result = func(*_args)	# _result 是被装饰的函数返回的真正结果 
            elapsed = time.time() - t0
            name = func.__name__
            args = ', '.join(repr(arg) for arg in _args) 
            result = repr(_result) 
            print(fmt.format(**locals())) # **locals()是为了在fmt中引用clocked的局部变量
            return _result 
		return clocked 
	return decorate 

if __name__ == '__main__':
	@clock(DEFAULT_FMT)
	def snooze(seconds):
		time.sleep(seconds)
	for i in range(3):
		snooze(.123)

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

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

相关文章

【避坑指南】快准狠!一键采购电子元器件(文末优惠券)

在采购元器件的过程中&#xff0c;经常会出现一些或这或那的情况&#xff0c;比如遇到假货问题、不具备专业知识、工作经验不够丰富、采购型号错误等等&#xff0c;因此采购下单如赌注&#xff0c;每个订单都下得心惊肉跳。 那么有哪些坑是可以避开的&#xff0c;又有什么方法可…

AI大模型加持,生成式搜索来了!

梦晨 发自 凹非寺量子位 | 公众号 QbitAI最近有两件事&#xff0c;让搜索引擎重回聚光灯下。百度发布“文心百中”&#xff0c;用AI大模型技术驱动的产业级搜索系统。构建企业内部搜索引擎的人力成本减少90%以上&#xff0c;同时只需要极低数据。几乎同一时间&#xff0c;OpenA…

算法---DFS和BFS

一 : 什么是DFS和BFS? 转载自 : 什么是DFS和BFS? 简介&#xff1a; 深度优先遍历&#xff08;Depth First Search, 简称 DFS&#xff09; 与广度优先遍历&#xff08;Breath First Search&#xff09;是图论中两种非常重要的算法&#xff0c;生产上广泛用于拓扑排序&#xf…

软考高级信息系统项目管理师如何备考?

从以下两个方面&#xff1a; 1.首先分析一下高项考试的各个科目&#xff1b; 2.如何备考高项&#xff1f; 高项考试有三个科目&#xff1a; 综合知识&#xff0c;案例分析&#xff0c;和论文。 一、综合知识 信息系统项目管理师上午综合知识科目范围广&#xff0c;知识点非…

【差分进化算法】基于适应度-距离-平衡的自适应引导差分进化 (FDB-AGDE) 算法附matlab代码

​✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法…

java工作流开源框架可以提高工作效率吗?

要想回答这个问题&#xff0c;就需要了解什么是java工作流开源框架&#xff0c;以及java工作流开源框架的主要特点是什么。随着大数据时代的拓展发展&#xff0c;低代码开发平台已经在数字化管理时代中深受欢迎&#xff0c;是做好数据管理和提升企业数字化发展步伐的重要工具。…

医疗机构 IT 管理员保护患者数据和隐私的 3 项必做之事

自疫情开始以来&#xff0c;医疗机构的信息存储与管理正面临着巨大的考验。患者的健康史&#xff0c;包括所有治疗、程序、处方、实验室测试和扫描报告&#xff0c;都以电子健康记录 (EHR) 的形式存储。尽管 EHR 更能提高患者病例的准确性&#xff0c;并帮助医生跟踪患者的医疗…

网络三层交换机部署实验

♥️作者&#xff1a;小刘在C站 ♥️每天分享云计算网络运维课堂笔记&#xff0c;疫情之下&#xff0c;你我素未谋面&#xff0c;但你一定要平平安安&#xff0c;一 起努力&#xff0c;共赴美好人生&#xff01; ♥️夕阳下&#xff0c;是最美的&#xff0c;绽放&#xff0c;愿…

排查cpu飚高问题-两种方案

cpu 飚高原因 频繁创建线程 线程内进行频繁计算 模拟代码 SpringBootApplicationEnableSchedulingpublic class CrawlBigDataApplication {public static void main(String[] args) { /*19*/ SpringApplication.run(CrawlBigDataApplication.class, (String[])args)…

【并发编程】Atomic类

一、介绍 在java.util.concurrent.atomic包下atomic一般指原子操作类&#xff0c;主要分为四种类型的原子更新类&#xff1a;原子更新基本类型、原子更新数组类型、原子更新引用和原子更新属性。 二、简单使用 1.AtomicInteger 通过synchronized关键字来保证原子性&#xf…

社交平台数据提取:Social Phone Extractor

Social Phone Extractor是一个功能强大且创新的程序&#xff0c;能够搜索和扫描在 Google / Bing / Yahoo 中索引并与最重要的社交网络&#xff08;如 Linkedin、Facebook、Twitter 和 Instagram&#xff09;相关的个人资料、帖子和文章的页面&#xff0c;然后捕获和推断&#…

ChatGPT介绍世界杯历史与编写足球游戏python程序

ChatGPT聊天机器人最近非常流行&#xff0c;是由OpenAI于本月发布的。花了一点时间注册了一个账号&#xff0c;如有需要帮助注册的可以随时与我交流。注册过程相对有一些复杂。 除了常规的聊天对话功能之外&#xff0c;ChatGPT聊天机器具备强大的文本生成能力&#xff0c;例如博…

【Android】Broadcast广播的使用

一、广播机制概述 通常情况下在学校的每个教室都会装有一个喇叭&#xff0c;这些喇叭是接入到学校广播室的。如果有重要通知&#xff0c;会发送一条广播来告知全校师生。为了便于发送和接收系统级别的消息通知&#xff0c;Android系统也引入了一套类似广播的消息机制。 Android…

股票撤单委托接口是如何操作的?

在交易的过程中&#xff0c;要先通过股票撤单委托接口获取委托单列表的数据&#xff0c;才可以进行撤单的操作&#xff0c;部分的数据会作为参数传递给撤单函数&#xff0c;下面来具体看看股票撤单委托接口是如何操作的&#xff1f; std::cout << " 撤单委托 \n&qu…

025_SSS_BeLFusion: Latent Diffusion for Behavior-Driven Human Motion Prediction

BeLFusion: Latent Diffusion for Behavior-Driven Human Motion Prediction 本文关注的问题是human motion prediction&#xff08;HMP&#xff09;&#xff0c;也就是在给定观测到的人体运动的前提下&#xff0c;预测人体的后续运动。本文的思路是&#xff0c;将人的behavio…

Linux文件服务NFS共享存储服务

作者简介&#xff1a;一名99年软件运维应届毕业生&#xff0c;正在自学云计算课程。宣言&#xff1a;人生就是B&#xff08;birth&#xff09;和D&#xff08;death&#xff09;之间的C&#xff08;choise&#xff09;&#xff0c;做好每一个选择。创作不易&#xff0c;动动小手…

高视医疗在港交所上市:IPO首日跌破发行价,高铁塔为控股股东

12月12日&#xff0c;高视医疗&#xff08;HK:02407&#xff09;在港交所上市。本次上市&#xff0c;高视医疗的发行价确定为每股发售股份51.40港元。据此计算&#xff0c;高视医疗预计募资约6.72亿港元。而招股书则显示&#xff0c;该公司预计募资净额约为2.83亿港元&#xff…

java 剑指 Offer 57 - II. 和为s的连续正数序列

题目所属分类 双指针的做法 O&#xff08;n)的时间复杂度 同时末尾有List<int[]> res 这种的转化成二位数组 原题链接 输入一个正整数 target &#xff0c;输出所有和为 target 的连续正整数序列&#xff08;至少含有两个数&#xff09;。 序列内的数字由小到大排列&…

Spring Boot自定义starters

一、简介 SpringBoot 最强大的功能就是把我们常用的场景抽取成了一个个starter&#xff08;场景 启动器&#xff09;&#xff0c;我们通过引入springboot 为我提供的这些场景启动器&#xff0c;我们再进行 少量的配置就能使用相应的功能。即使是这样&#xff0c;springboot也不…

R语言绘制复杂抽样设计数据cox回归生存曲线(Kaplan-Meier)

上期咱们已经介绍了咱们绘制复杂抽样设计数据的基础图形&#xff0c;今天咱们来介绍一下咱们绘制复杂抽样设计cox回归生存曲线(Kaplan-Meier)。 废话不多说咱们先导入数据和R包 library(survey) pbc<-read.csv("E:/r/test/pbc.csv",sep,,headerTRUE) 这是一个原…