Python之路11:函数之命名空间,闭包,生成器,迭代器

命名空间

名称空间是存放变量名和赋值绑定关系的地方

名称空间共 3 种:

  • locals: 是函数内的名称空间,包括局部变量和形参
  • globals: 全局变量,函数定义所在模块的名称空间
  • builtins: 内置模块的名称空间

不同变量的作用域不同是由这个变量所在的命名空间决定的
作用域范围:

  • 全局变量:全局有效,全局存活
  • 局部变量:局部有效,局部存活

查看作用域的方法:globals(), locals()
作用域的查找规则:LEGB

闭包

闭包:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层的作用域

def func():
    name = 'jack'

  def inner():
        print('函数变量:', name)

    return inner

f = func()

f()
# 返回结果: 函数变量:jack

生成器

列表生成式

# 将列表中的每一个值都进行自乘
# 可以通过列表生成式来完成
>>> a = list(range(10))
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a = [i*i for i in a]
>>> a
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

# 以上就是通过简单的列表生成式来实现的

生成器:
通过列表生成式,可以直接创建一个列表,但是受到内存限制,列表的容量是有限的。
列表元素通过某种算法,再循环中不断推算出后续的元素,这种一边循环一边计算的机制,称为生成器:generator

# 要创建一个 generator 很简单,就是将列表生成式中的 [] 改成 ()
>>> l = [i*i for i in range(10)]
>>> l
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x*x for x in range(10))
>>> g
<generator object <genexpr> at 0x7f77ffd6cdb0>

# 创建 l 和 g 的区别就是最外层的[] 和 (), l 是一个list,而 g 是 generator,可以直接打印获取 list 所有元素,但 generator 需要通过 next() 一个一个获取
>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
16
>>> next(g)
25
>>> next(g)
36
>>> next(g)
49
>>> next(g)
64
>>> next(g)
81
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
# 直到最后获取不到抛出 StopIteration 错误
# 但用上面 next(g) 获取太麻烦,可以通过 for 循环
>>> g = (x*x for x in range(10))
>>> for n in g:
...   print(n)
... 
0
1
4
9
16
25
36
49
64
81
# 而且这样也不会报错

著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数可由前两个数相加所得
斐波拉契数列通过列表生成式无法写出来,但是可以通过函数打印出来

def fin(max):
    n, a, b = 0, 0, 1
    
    while n < max:
        print b
        a, b = b, a+b
        
        n += 1
    return 'done'

data = fin(5)
print(data)
# 执行结果为
1
1
2
3
5

# 上面 fin 函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素推算出后续任意的元素
# 要讲上面函数变成 generator,只需要把 print b 改为 yield b
def fin(max):
    n, a, b = 0, 0, 1
    
    while n < max:
        # print b
        yield b
        a, b = b, a+b
        
        n += 1
    return 'done'

data = fin(5)
print(data)
# 执行结果:<generator object fib at 0x00000232F05DEF68>

# 这是函数中定义 generator 的一种方法,如果一个函数内包含 yield 字段,那么这个函数就不再是一个普通函数,而是一个 generator

# generator执行过程和函数是不同的,函数执行是顺序执行,需要 return语句或者最后一行语句返回,而 generator函数,则是每次调用 next() 的时候执行,遇到 yield语句返回,如没有next(),函数执行到 yield 是就停止不再继续执行了,再次被 next() 调用时从上次返回的 yield 语句继续执行下,在次遇到 yield 语句返回并停止

迭代器

  • 可迭代对象
    可以直接作用于 for 循环的对象称为可迭代对象:Iterable
    可直接作用于 for 循环的数据类型有以下几类:
    一类是集合数据类型:list ,tuple, dict, set, str 等
    一类是 generator,包括生成器和带 yield 的generator functions
    可以使用 isinstance() 判断一个对象是否是 Iterable(可迭代)对象
>>> from collections import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance((), Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance(123, Iterable)  # 整数就不是可迭代对象
False

而生成器不但可以作用于 for 循环,还可以被 next() 函数不断的调用并返回下一个值,直到最后抛出 Stoplteration 错误并返回

  • 迭代器
    可以被 next() 函数调用并不断返回下一个值的对象称为 迭代器:Iterator
    可以用 isinstance()判断一个对象是否是 Iterator(迭代)对象:
>>> isinstance((i for i in range(10)), Iterator)
True
>>> isinstance([], Iterator)
False
>>> isinstance('abc', Iterator)
False

# 生成器都是 Iterator(迭代器)对象,但 list,str, dict 虽然是 Iterable(可迭代对象), 但不是 Iterator(迭代器)

可以通过 iter() 函数把 Iterable 变成 Iterator

>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True

Python 中 Iterator 对象表示的是一个数据流,Iterator 对象可以被 next() 函数调用并不断返回下一个数据,直到没有数据抛出 StopIteration 错误,可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

  • 总结

凡是可作用于 for 循环的对象都是 Iterable 类型
凡是可作用于 next() 函数的对象都是 Iterator 类型,它们表示一个惰性计算的序列
集合类型如 list,dict,str等都是 Iterable,但不是 Iterator, 但是可以通过 iter() 函数获得一个 Iterator 对象
python3 的 for 循环本质上就是通过不断调用 next() 函数实现的

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容