博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
函数名的运用、闭包以及迭代器
阅读量:6905 次
发布时间:2019-06-27

本文共 7623 字,大约阅读时间需要 25 分钟。

函数名的运用

函数名是一种特殊的变量,函数名加上括号后表示函数执行,除此之外,函数名还可以进行如下几条操作:

1. 作为变量赋值

1 def func():2     print(666)3 4 5 f1 = func6 f2 = f17 f2()   # 打印666

 

2. 作为容器内数据类型的元素

应用场景:需要调用多个函数的时候

方法一:

1 def func1(): 2     print(111) 3  4  5 def func2(): 6     print(222) 7  8  9 def func3():10     print(333)11 12 13 func_list = [func1, func2, func3]14 for func in func_list:15     func()

执行结果

111222333

方法一里面是按照顺序执行函数,如果我们指定要执行某些函数呢,来看方法二

方法二:

1 def func1(): 2     print(111) 3  4  5 def func2(): 6     print(222) 7  8  9 def func3():10     print(333)11 12 13 dic = {14     1: func1,15     2: func2,16     3: func317 }18 19 for k in dic.keys():20     dic[k]()

把函数放在字典里,想执行哪个就执行哪个

 

3. 作为函数的参数

1 def func(f):2     f()3 4 5 def func1():6     print(222)7 8 9 func(func1)    # 222

 

4. 作为函数的返回值

1 def func(x):2     return x3 4 5 def func1():6     print("in func1")7 8 9 func(func1)()   # in func1

实际上,函数名是第一类对象,第一类对象的特点:

1. 可在运行期间创建

2. 可用作函数参数或返回值

3. 可存入变量的实体

笼统来说,第一类变量就是普通变量

 

闭包

首先抛出一个问题:为什么要有闭包?来看两个例子

实例一:

1 def func(step): 2     num = 1 3     num += step 4     print(num) 5  6  7 j = 0 8 while j < 5: 9     func(3)10     j += 1

执行结果

44444

上面是没有使用闭包的情况,下面来看使用闭包的情况

实例二:

1 def wrapper(step): 2     num = 1 3  4     def inner(): 5         nonlocal num    # 引用num,与return inner形成闭包 6         num += step     # 此时num在inner执行完了之后不会被清理,会作为下一次的起始值 7         print(num) 8     return inner 9 10 11 f = wrapper(3)12 j = 013 while j < 5:14     f()15     j += 1

执行结果

47101316

首先来看函数的结构,这是一个嵌套函数,wrapper函数里面嵌套一个inner函数,要知道,在函数外面是不能直接调用内层函数的,那么我们怎么做呢?答案是通过return,我们可以把外层函数的返回值设置为内层函数名,这样层层返回,就可以在外界调用任意位置的内层函数。可是这和我闭包又有什么关系呢?当然有啦,比如说我想调用inner函数对num进行操作,那么我是不是得让inner的外层函数wrapper的返回值设置为inner啊,其次还得在inner内部设置nonolocal,要不然就不能对num就行操作,其实在内层函数引用外层函数的变量,然后外层函数返回内层函数这样就形成了闭包,总结一下,关于闭包:

1. 闭包是内层函数对外层函数(非全局)变量的引用

2. 闭包只存在于内层函数中

3. 函数都要逐层返回,最终返回给最外层函数

下面来看一个例子,

1 def func(n):  # 相当于n=name2     def inner():3         print(n)4 5     return inner6 7 8 name = "Hanser"9 f = func(name)

这个是不是闭包呢,答案是肯定的,这里的n传入func里面后是存放于func的名称空间里的,inner的print(n)就是内层函数对外层函数的引用,然后func的返回值是内层函数名inner,所以这个是闭包。这样判断是不是有点麻烦啊,那么有没有简单的办法呢,有的,python提供了判断闭包的方法: __closure__,来看代码

1 def func1(a): 2     n = 1 3  4     def func2(): 5         nonlocal n 6         n += a 7  8         def func3(): 9             nonlocal n10             n *= a11             print(n)12         return func313     return func214 15 16 f = func1(3)  # f = func217 print(f.__closure__[0].cell_contents)    # 获取引用的外层函数的变量,如果能获取到,就是闭包18 print(f.__closure__[1].cell_contents)    #19 # print(f.__closure__[2].cell_contents)    # 报错,没有第三个20 21 print("------我是华丽丽的分割线-------")22 23 f1 = func1(3)()     # f1 = func324 print(f1.__closure__[0].cell_contents)25 print(f1.__closure__[1].cell_contents)26 # print(f1.__closure__[2].cell_contents)     # 报错,没有第三个

执行结果

31------我是华丽丽的分割线-------34

总结一下,判断步骤:

(1)找到要判断的内层函数(line16和line23)

(2)获取该内层函数引用的外层函数的变量

(3)能获取到,是闭包;否则不是闭包

这里有一个小知识点:获取到的引用的外层函数的变量顺序:   传入的参数 外层函数定义的变量

 

闭包作用

正常程序执行时,遇到函数,随着函数的结束而关闭临时名称空间,闭包的本质就是闭包会创建一个空间,这个空间不会随着函数的结束而关闭,因而之后可以继续调用,这是非常有用的,闭包的应用场景:

1. 装饰器

2. 爬虫

来看一个爬虫实例

1 from urllib.request import urlopen 2  3  4 def but(): 5     content = urlopen("https://book.douban.com/annual/2018?source=navigation#1").read()    # 获取网页源代码 6  7     def get_content(): 8         return content 9     return get_content10 11 12 fn = but()13 print(id(fn()))    # 250570623153614 print(id(fn()))    # 2505706231536  两个id一样,证明第一次获取的内容没有消失,第二次是直接调用第一次的内容15 content1 = fn()   # 获取内容16 print(content1.decode("utf-8"))   # 解码

 

执行结果

25057062315362505706231536            
豆瓣2018年度读书榜单
cover

content内容不会随着but函数的结束而消失,这个非常有用,因为获取内容后还要进行筛选等操作,而请求一次因为网络延时等原因是非常耗时间的,有了闭包,就不用再次去请求获取内容,节省了很多时间。

迭代器

在讲迭代器之前,先来看一下可迭代对象,什么是可迭代对象呢,可迭代对象的定义是:内部含有__iter__方法的对象。

判断方法

方法一:

s1 = "hello"print(dir(s1))   # 返回的方法里面有__iter__()就是可迭代对象print("__iter__" in dir(s1))   # True

执行结果

['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']True

 

f = open("goods.txt", encoding="utf-8", mode="r")print(dir(f))print("__iter__" in dir(f))

执行结果

['_CHUNK_SIZE', '__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_checkClosed', '_checkReadable', '_checkSeekable', '_checkWritable', '_finalizing', 'buffer', 'close', 'closed', 'detach', 'encoding', 'errors', 'fileno', 'flush', 'isatty', 'line_buffering', 'mode', 'name', 'newlines', 'read', 'readable', 'readline', 'readlines', 'seek', 'seekable', 'tell', 'truncate', 'writable', 'write', 'writelines']True

 

方法二:

1 from collections import Iterable2 from collections import Iterator3 l1 = [1, 2, 3]4 print(isinstance(l1, Iterable))  # 判断是否是可迭代对象5 print(isinstance(l1, Iterator))  # 判断是否是迭代器

执行结果

TrueFalse

那么什么是迭代器呢,迭代器在可迭代对象的基础上增加了__next__方法(取值用),可迭代对象可以转化成迭代器,你猜的没错,就是用__iter__方法

1 obj = s1.__iter__()    # 方法一:可迭代对象转化成迭代器2 # obj = iter(s1)         # 方法二:可迭代对象转化成迭代器3 print(obj.__next__())   # a4 print(obj.__next__())   # b5 print(obj.__next__())   # c6 print(obj.__next__())   # d7 print(obj.__next__())   # 报错 StopIteration

执行结果

abcd

分析上述代码可以发现规律,__next__方法每次只取一个值,当取值个数超出时会报错,此外__next__()方法可以用next()方法代替。

1 s2 = [1, 2, 3]2 obj = s2.__iter__()3 print(obj.__next__())4 print(next(obj))   # 与__next__一样

执行结果

12

 

运用迭代器和while循环还可以模拟for循环,来看代码

1 lst = [1, 2, 3, 4, 5]2 obj = iter(lst)3 while True:4     try:5         print(next(obj))6     except StopIteration:7         break

执行结果

12345

试试字典

1 dic = {
"name": "hanser", "age": 18, "height": 148}2 obj = iter(dic)3 while True:4 try:5 print(next(obj))6 except StopIteration:7 break

执行结果

nameageheight

对字典直接取值取出的是key,如果想取value或者键值对把dic换成dic.values()或dic.items()就行

1 dic = {
"name": "hanser", "age": 18, "height": 148}2 obj = iter(dic.values())3 while True:4 try:5 print(next(obj))6 except StopIteration:7 break

执行结果

hanser18148

总结一下:

可迭代对象:内部含有__iter__方法的对象

  str, list, tuple, dic, set, range(), 文件句柄都是可迭代对象

迭代器:内部含有__iter__方法和__next__方法的对象

  str, list, tuple, dic, set, range()不是迭代器

  文件句柄是迭代器

判断可迭代对象和迭代器的方法

  "__iter__" in dir(s), "__next__" in dir(s)

  isinstance(s, Iterable), isinstance(s, Iterator)

可迭代对象转化成迭代器

  __iter__(), iter()

迭代器特点

  节省内存,迭代器只存放下一个对象的值

  惰性机制,next一次取一个值

  单项取值,不走回头路

利用迭代器和while循环可以模拟for循环

 

转载于:https://www.cnblogs.com/zzliu/p/10209603.html

你可能感兴趣的文章
mysql 增加列,修改列名、列属性,删除列语句
查看>>
CSS实例:图片导航块
查看>>
RedHat下MySQL 5.6 安装、维护
查看>>
用Python把PDF文件转换成Word文档
查看>>
Objective-C SQLite数据库封装FMDB的介绍
查看>>
记 移动端页面中莫名其妙的渲染BUG
查看>>
[转载]深入理解Python异步编程(上)
查看>>
java版MD5签名工具类
查看>>
python中的常用模块
查看>>
百度UEditor(富文本编辑器)的基础用法
查看>>
天空的另一半
查看>>
面向 Photoshop 的英特尔® Texture Works 插件
查看>>
Docker - 配置国内加速器加速镜像下载
查看>>
批量删除Sql Server对象(表,存储过程,触发器)
查看>>
[LeetCode]Remove Linked List Elements
查看>>
QTableWidget简单操作
查看>>
2018-2019-1 20165318《信息安全系统设计基础》第六周课上测试
查看>>
mongodb索引
查看>>
HDU 1053 Entropy
查看>>
Jenkins+Gradle+Sonar进行Java项目代码分析
查看>>