- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
摘自<流畅的python> 第七章 函数装饰器和闭包 。
实现一个函数(可以不是函数)avg,计算不断增加的系列值的平均值,效果如下 。
def avg(...):
pass
avg(10) =>返回10
avg(20) =>返回10+20的平均值15
avg(30) =>返回10+20+30的平均值20
跟 Python常见面试题015.请实现一个如下功能的函数 有点类似,但又不太一样 。
关键是你需要有个变量来存储历史值 。
参考代码 。
class Average():
def __init__(self):
self.series = []
def __call__(self, value):
self.series.append(value)
return sum(self.series)/len(self.series)
avg = Average()
print(avg(10))
print(avg(20))
print(avg(30))
avg是个Average的实例 。
avg有个属性series,一开始是个空列表 。
__call__ 使得avg对象可以像函数一样调用 。
调用的时候series会保留,因为series只在第一次初始化的时候置为空列表 。
下面的事情就变得简单了 。
参考代码 。
def make_average():
series = []
def averager(value):
series.append(value)
return sum(series)/len(series)
return averager
avg = make_average()
print(avg(10))
print(avg(20))
print(avg(30))
仔细对比2个代码,你会发现相似度是极高的 。
一个是类,一个是函数 。
类中存储历史值的是self.series,函数中的是series 局部变量 。
类实例能调用是实现了 __call__ ,函数的实现中,avg是make_average()的返回值averager,是个函数名,所以它也能调用 。
闭包closure定义:
外函数
中定义了一个 内函数
临时变量
内函数的引用
以上面的为例 。
def make_average(): # 外函数
series = [] # 临时变量(局部变量)
def averager(value): # 内函数
series.append(value)
return sum(series)/len(series)
return averager # 返回内函数的引用
下面这些话你可能听的云里雾里的,姑且听一下.
series 是 make_averager 函数的局部变量,因为那个函数的定义体中初始化了series:series = [] 。
调用 avg(10) 时,make_averager 函数已经返回了,而它的本地作用域也一去不复返了 。
在 averager 函数中,series 是自由变量(free variable)。这是一个技术术语,指未在本地作用域中绑定的变量 。
averager 的闭包延伸到那个函数的作用域之外,包含自由变量 series 的绑定 。
from dis import dis
dis(make_average)
2 0 BUILD_LIST 0
2 STORE_DEREF 0 (series)
3 4 LOAD_CLOSURE 0 (series)
6 BUILD_TUPLE 1
8 LOAD_CONST 1 (<code object averager at 0x000002225DD1CBE0, file "<ipython-input-1-a43a8601eedd>", line 3>)
10 LOAD_CONST 2 ('make_average.<locals>.averager')
12 MAKE_FUNCTION 8 (closure)
14 STORE_FAST 0 (averager)
6 16 LOAD_FAST 0 (averager)
18 RETURN_VALUE
Disassembly of <code object averager at 0x000002225DD1CBE0, file "<ipython-input-1-a43a8601eedd>", line 3>:
4 0 LOAD_DEREF 0 (series)
2 LOAD_METHOD 0 (append)
4 LOAD_FAST 0 (value)
6 CALL_METHOD 1
8 POP_TOP
5 10 LOAD_GLOBAL 1 (sum)
12 LOAD_DEREF 0 (series)
14 CALL_FUNCTION 1
16 LOAD_GLOBAL 2 (len)
18 LOAD_DEREF 0 (series)
20 CALL_FUNCTION 1
22 BINARY_TRUE_DIVIDE
24 RETURN_VALUE
读懂上面的,不是人干的事情,不过你依然有可能 。
https://docs.python.org/zh-cn/3/library/dis.html#bytecodes
怎么样不云里雾里呢 。
查看 avg.__code__ 属性 。
[_ for _ in dir(avg.__code__) if _[:2]=='co']
['co_argcount',
'co_cellvars',
'co_code',
'co_consts',
'co_filename',
'co_firstlineno',
'co_flags',
'co_freevars',
'co_kwonlyargcount',
'co_lnotab',
'co_name',
'co_names',
'co_nlocals',
'co_posonlyargcount',
'co_stacksize',
'co_varnames']
官方解释 。
属性 | 描述 |
---|---|
co_argcount | 参数数量(不包括仅关键字参数、* 或 ** 参数) |
co_code | 原始编译字节码的字符串 |
co_cellvars | 单元变量名称的元组(通过包含作用域引用) |
co_consts | 字节码中使用的常量元组 |
co_filename | 创建此代码对象的文件的名称 |
co_firstlineno | 第一行在Python源码的行号 |
co_flags | CO_* 标志的位图,详见 此处 |
co_lnotab | 编码的行号到字节码索引的映射 |
co_freevars | 自由变量的名字组成的元组(通过函数闭包引用) |
co_posonlyargcount | 仅限位置参数的数量 |
co_kwonlyargcount | 仅限关键字参数的数量(不包括 ** 参数) |
co_name | 定义此代码对象的名称 |
co_names | 局部变量名称的元组 |
co_nlocals | 局部变量的数量 |
co_stacksize | 需要虚拟机堆栈空间 |
co_varnames | 参数名和局部变量的元组 |
通过 __code__ 分析 。
def make_average():
series = []
def averager(value):
series.append(value)
total = sum(series)
return total/len(series)
return averager
avg = make_average()
avg.__code__.co_varnames # 参数名和局部变量的元组
# ('value', 'total') # value是参数,total是局部变量名
avg.__code__.co_freevars
# ('series',) # 自由变量的名字组成的元组(通过函数闭包引用)
结合 avg.__closure__ 。
avg.__closure__
# (<cell at 0x000002225FA4DC70: list object at 0x000002225EE35600>,)
# 这是个cell对象,list对象
len(avg.__closure__) # 1
avg.__closure__[0].cell_contents # [] 因为你还没调用
avg(10)
avg(20)
avg(30)
avg.__closure__[0].cell_contents # [10, 20, 30] 保存着真正的值
闭包是一种函数,它会保留定义函数时存在的自由变量的绑定,这样调用函数时,虽然定义作用域不可用了,但是仍能使用那些绑定.
只有嵌套在其他函数中的函数才可能需要处理不在全局作用域中的外部变量 。
前面的make_averager 函数的方法效率不高 。
因为我们把所有值存储在历史数列中,然后在每次调用 averager 时使用 sum 求和 。
更好的实现方式是,只存储目前的总值和元素个数,然后使用这两个数计算均值 。
所以你可能这样实现 。
def make_average():
total = 0
length = 0
def averager(value):
total = total + value
length = length + 1
return total/length
return averager
avg = make_average()
执行avg(10)的时候你就会报错 。
UnboundLocalError Traceback (most recent call last)
<ipython-input-11-ace390caaa2e> in <module>
----> 1 avg(10)
<ipython-input-9-eaa25222e808> in averager(value)
4 def averager(value):
5 # nonlocal total,length
----> 6 total = total + value
7 length = length + 1
8 return total/length
UnboundLocalError: local variable 'total' referenced before assignment
这个问题你应该看到过,在前面的面试题002中看到过这样的错误 。
关键的错误是在于 。
total = total + value
length = length + 1
这样的赋值会把total和length都变成局部变量 。
from dis import dis
dis(make_average)
2 0 LOAD_CONST 1 (0)
2 STORE_FAST 0 (total)
3 4 LOAD_CONST 1 (0)
6 STORE_FAST 1 (length)
4 8 LOAD_CONST 2 (<code object averager at 0x0000026A8ED0E660, file "<ipython-input-12-12a610cc685c>", line 4>)
10 LOAD_CONST 3 ('make_average.<locals>.averager')
12 MAKE_FUNCTION 0
14 STORE_FAST 2 (averager)
8 16 LOAD_FAST 2 (averager)
18 RETURN_VALUE
Disassembly of <code object averager at 0x0000026A8ED0E660, file "<ipython-input-12-12a610cc685c>", line 4>:
5 0 LOAD_FAST 1 (total)
2 LOAD_FAST 0 (value)
4 BINARY_ADD
6 STORE_FAST 1 (total)
6 8 LOAD_FAST 2 (length)
10 LOAD_CONST 1 (1)
12 BINARY_ADD
14 STORE_FAST 2 (length)
7 16 LOAD_FAST 1 (total) #此处 LOAD_FAST 加载局部变量
18 LOAD_FAST 2 (length)
20 BINARY_TRUE_DIVIDE
22 RETURN_VALUE
是对数字、字符串、元组等不可变类型来说,只能读取,不能更新。如果尝试重新绑定,例如 count = count + 1,其实会隐式创建局部变量 count。这样,count 就不是自由变量了,因此不会保存在闭包中 。
为了解决这个问题,Python 3 引入了 nonlocal 声明。它的作用是把变量标记为自由变量,即使在函数中为变量赋予新值了,也会变成自由变量。如果为 nonlocal 声明的变量赋予新值,闭包中保存的绑定会更新.
解决的代码 。
def make_average():
total = 0
length = 0
def averager(value):
nonlocal total,length
total = total + value
length = length + 1
return total/length
return averager
avg = make_average()
# 你就可以avg(10)这样了~
最后此篇关于Python常见面试题016.请实现如下功能|谈谈你对闭包的理解的文章就讲到这里了,如果你想了解更多关于Python常见面试题016.请实现如下功能|谈谈你对闭包的理解的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
背景: 我最近一直在使用 JPA,我为相当大的关系数据库项目生成持久层的轻松程度给我留下了深刻的印象。 我们公司使用大量非 SQL 数据库,特别是面向列的数据库。我对可能对这些数据库使用 JPA 有一
我已经在我的 maven pom 中添加了这些构建配置,因为我希望将 Apache Solr 依赖项与 Jar 捆绑在一起。否则我得到了 SolarServerException: ClassNotF
interface ITurtle { void Fight(); void EatPizza(); } interface ILeonardo : ITurtle {
我希望可用于 Java 的对象/关系映射 (ORM) 工具之一能够满足这些要求: 使用 JPA 或 native SQL 查询获取大量行并将其作为实体对象返回。 允许在行(实体)中进行迭代,并在对当前
好像没有,因为我有实现From for 的代码, 我可以转换 A到 B与 .into() , 但同样的事情不适用于 Vec .into()一个Vec . 要么我搞砸了阻止实现派生的事情,要么这不应该发
在 C# 中,如果 A 实现 IX 并且 B 继承自 A ,是否必然遵循 B 实现 IX?如果是,是因为 LSP 吗?之间有什么区别吗: 1. Interface IX; Class A : IX;
就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用资料或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
我正在阅读标准haskell库的(^)的实现代码: (^) :: (Num a, Integral b) => a -> b -> a x0 ^ y0 | y0 a -> b ->a expo x0
我将把国际象棋游戏表示为 C++ 结构。我认为,最好的选择是树结构(因为在每个深度我们都有几个可能的移动)。 这是一个好的方法吗? struct TreeElement{ SomeMoveType
我正在为用户名数据库实现字符串匹配算法。我的方法采用现有的用户名数据库和用户想要的新用户名,然后检查用户名是否已被占用。如果采用该方法,则该方法应该返回带有数据库中未采用的数字的用户名。 例子: “贾
我正在尝试实现 Breadth-first search algorithm , 为了找到两个顶点之间的最短距离。我开发了一个 Queue 对象来保存和检索对象,并且我有一个二维数组来保存两个给定顶点
我目前正在 ika 中开发我的 Python 游戏,它使用 python 2.5 我决定为 AI 使用 A* 寻路。然而,我发现它对我的需要来说太慢了(3-4 个敌人可能会落后于游戏,但我想供应 4-
我正在寻找 Kademlia 的开源实现C/C++ 中的分布式哈希表。它必须是轻量级和跨平台的(win/linux/mac)。 它必须能够将信息发布到 DHT 并检索它。 最佳答案 OpenDHT是
我在一本书中读到这一行:-“当我们要求 C++ 实现运行程序时,它会通过调用此函数来实现。” 而且我想知道“C++ 实现”是什么意思或具体是什么。帮忙!? 最佳答案 “C++ 实现”是指编译器加上链接
我正在尝试使用分支定界的 C++ 实现这个背包问题。此网站上有一个 Java 版本:Implementing branch and bound for knapsack 我试图让我的 C++ 版本打印
在很多情况下,我需要在 C# 中访问合适的哈希算法,从重写 GetHashCode 到对数据执行快速比较/查找。 我发现 FNV 哈希是一种非常简单/好/快速的哈希算法。但是,我从未见过 C# 实现的
目录 LRU缓存替换策略 核心思想 不适用场景 算法基本实现 算法优化
1. 绪论 在前面文章中提到 空间直角坐标系相互转换 ,测绘坐标转换时,一般涉及到的情况是:两个直角坐标系的小角度转换。这个就是我们经常在测绘数据处理中,WGS-84坐标系、54北京坐标系
在软件开发过程中,有时候我们需要定时地检查数据库中的数据,并在发现新增数据时触发一个动作。为了实现这个需求,我们在 .Net 7 下进行一次简单的演示. PeriodicTimer .
二分查找 二分查找算法,说白了就是在有序的数组里面给予一个存在数组里面的值key,然后将其先和数组中间的比较,如果key大于中间值,进行下一次mid后面的比较,直到找到相等的,就可以得到它的位置。
我是一名优秀的程序员,十分优秀!