Python闭包怎么实现代替类,如何访问定义闭包内的变量
Admin 2022-06-28 群英技术资�
有时我们会定义只有一个方法(除了__init__()
之外)的类,而这种类可以通过使用闭包�closure
)来替代。闭包是被外层函数包围的内层函数,它能够获取外层函数范围中的变量(即使外层函数已执行完毕)。因此闭包可以保存额外的变量环境,用于在函数调用时使用。考虑下面这个例子,这个类允许用户通过某种模板来获取URL�
from urllib.request import urlopen class UrlTemplate: def __init__(self, template) -> None: self.template = template def open(self, **kwargs): return urlopen(self.template.format_map(kwargs)) yahoo = UrlTemplate('https://finance.yahoo.com/d/quotes.csv?s={names}&f={fields}') for line in yahoo.open(names='IBM,AAPL,FB', fields = 'sllclv'): print(line.decode('utf-8'))
这个类可以用一个简单的函数来替代:
def urltempalte(template): # 后面用对象再次调用函数的时候传入kwargs def opener(**kwargs): return urlopen(template.format_map(kwargs)) return opener yahoo = urltempalte('https://finance.yahoo.com/d/quotes.csv?s={names}&f={fields}') for line in yahoo(names='IBM,AAPL,FB', fields = 'sllclv'): print(line.decode('utf-8'))
在许多情况下,我们会使用只有单个方法的类的原因是需要保存额外的状态给类方法使用。我们上面提到的UrlTemplate类的唯一目的就是�template
的值保存在某处,然后在open()
方法中使用它。而使用闭包来解决该问题会更加简短和优雅,我们使用opener()函数记住参数template
的值,然后再随后的调用中使用该值�
所以大家在编写代码中需要附加额外的状态给函数时,一定要考虑使用闭包�
我们知道闭包内层可以用来存储函数需要用到的变量环境。接下来,我们可以通过函数来扩展闭包,使得在闭包内层定义的变量可以被访问和修改�
一般来说,闭包内层定义的变量对外界来说是完全隔离的,如果想要访问和修改它们,需要编写存取函�(accessor function
, �getter/setter
方法),并将它们做为函数属性附加到闭包上来提供对内层变量的访问支持�
def sample(): n = 0 # 闭包函数 def func(): print("n =", n) # 存取函数(accessor function),即getter/setter方法 def get_n(): return n def set_n(value): # 必须要加nolocal才能修改内层变量 nonlocal n n = value # 做为函数属性附� func.get_n = get_n func.set_n = set_n return func
该算法测试运行结果如下:
f = sample() f() # n = 0 f.set_n(10) f() # n = 10 print(f.get_n()) # 10
可以看到�get_n()
�set_n()
工作起来很像实例的方法。注意一定要将get_n()和set_n()做为函数属性附加上去,否则在调�set_n()
�get_n()
就会报错�'function' object has no attribute 'set_n'
�
如果我们希望让闭包完全模拟成类实例,我们需要架构内层函数拷贝到一个实例的字典中然后将它返回�
示例如下�
import sys class ClosureInstance: def __init__(self, locals=None) -> None: if locals is None: locals = sys._getframe(1).f_locals # Update instance dictionary with callables self.__dict__.update( (key, value) for key, value in locals.items() if callable(value) ) # Redirect special methods def __len__(self): return self.__dict__['__len__']() # Example use def Stack(): items = [] def push(item): items.append(item) def pop(): return items.pop() def __len__(): return len(items) return ClosureInstance()
下面展示了对应的测试结果�
s = Stack() print(s) # <__main__.ClosureInstance object at 0x101efc280> s.push(10) s.push(20) s.push('Hello') print(len(s)) # 3 print(s.pop()) # Hello print(s.pop()) # 20 print(s.pop()) # 10
用闭包模型类的功能比传统的类实现方法要快一些。比如我们用下面这个类做为测试对比�
class Stack2: def __init__(self) -> None: self.items = [] def push(self, item): self.items.append(item) def pop(self): return self.items.pop() def __len__(self): return len(self.items)
下面是我们的测试结果�
from timeit import timeit s = Stack() print(timeit('s.push(1);s.pop()', 'from __main__ import s')) # 0.98746542 s = Stack2() print(timeit('s.push(1);s.pop()', 'from __main__ import s')) # 1.07070521
可以看到采用闭包的版本要快大�8%。因为对于实例而言,测试话费的大部分时间都在对实例变量的访问上,而闭包要更快一些,因为不用涉及额外的self变量�
不过这种奇技淫巧在代码中还是要谨慎使用,因为相比一个真正的类,这种方法其实是相当怪异的。像继承、属性、描述符或者类方法这样的特性在这种方法都是无法使用的。而且我们还需要一些花招才能让特殊方法正常工作(比如我们上�ClosureInstance中对__len__()
的实现)。不过,这仍然是一个非常有学术价值的例子,它告诉我们对闭包内部提供访问机制能够实现怎样的功能�
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:[email protected]进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容�
猜你喜欢
这篇文章主要介绍了Python可视化神器pyecharts之绘制箱形图,箱形图(Box-plot)又称为盒须图、盒式图或箱线图,是一种用作显示一组数据分散情况资料的统计图,因形状如箱子而得�
这篇文章主要介绍了python中的format是什么意思?format怎么用?今天小编就为大家介绍一下format用法,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐�
这篇文章主要介绍了Python 切片为什么不会索引越界?切片(slice)是 Python 中一种很有特色的特性,在正式开始之前,我们先来从关于切片的相关知识开始介绍,感兴趣的小伙伴一起参考参考呀</P><P>
这篇文章主要给大家分享python从本地读取mnist数据集的方法,mnist手写数字数据集在机器学习中很常见的,因此本文给大家分享读取mnist数据集的方法做个参考,需要的朋友可以了解看看�
本文主要介绍了python replace 空格数据处理的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
成为群英会员,开启智能安全云计算之旅
立即注册Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所�
增值电信经营许可证 : B1.B2-20140078