注意:global是把该变量声明为全局变量,而nonlocal是把该变量声明为上一级外部函数的局部变量
x = -1 def outer(): x = 0 def inner(): nonlocal x x = 1 print('inner:', x) inner() print('outer:', x) outer() print('global:', x)! x = -1 def outer(): x = 0 def inner(): global x x = 1 print('inner:', x) inner() print('outer:', x) outer() print('global:', x)可迭代协议:凡是含有__iter__方法的都是可迭代的,(在python中,可迭代的数据类型有字符串,列表,元组,字典,集合,枚举,range等,int和布尔类型不包含__iter__方法) 迭代器协议:凡是含有__iter__和__next__方法的都是迭代器 For循环的实质就是在调用__next__方法 l = [1, 2, ‘8hjg’, 0] for i in l: pass ‘’’
for循环的实质为:
iterator = l.iter()#可迭代的数据类型使用__iter__方法后,就返回了一个迭代器iterator.next()#对于迭代器使用__next__方法后,相当于从迭代器中一个一个取值当出现false后,执行结束 ‘’’ 迭代器的优势: a)迭代器中的值会一个一个的被取出 b)节省内存空间,循环一次产生一个变量,而不是一次性全部产生 尤其是对于文件句柄和range()而言,特别节省内存空间生成器的本质还是迭代器,凡是含有yield关键字的函数都是生成器函数 0)yield不能与return混用,且必须写在函数内部 1)生成器函数在被执行后会返回一个生成器作为返回值 ,而函数内部的语句并不会被执行 2)一个简单的生成器函数的例子  打印结果如下图: 简单介绍一下return和yield的区别吧: 当一个程序执行到return时,程序结束,return之后的语句不会再执行,换句话说,return就是程序结束的标志,而yield不会引起程序结束,当程序运行到yield时,相当于暂停了,如果下一次又调用函数,需要执行yield时,接着上次暂停的地方继续执行(不知道这么理解对不对) 我个人喜欢用一些生活中的例子来进行类比: 执行程序相当于在跑马拉松,当出现return时,相当于选手退出比赛,当出现yield时,相当于选手暂停休息了一会,当生成器函数被再次调用时,选手又接着上次暂停的地方继续开始跑,直到出现下一个yield 说到跑步的例子,我就不得不再说一下break和continue的区别了: 这也不能说是我思维混乱,只能说思维比较跳跃吧 由于break和continue一般是在循环中使用的,所以类比成在操场跑圈吧 break相当于选手直接退赛,不再参与跑圈这一运动,而continue相当于选手放弃了这圈中没有跑完的这一部分,直接从下一圈的起点开始跑
0)一个实例:
def genreatoion(): print(123) count = yield 1 print('=====', count) yield 2 g = genreatoion() ret = g.__next__() print(ret) ret = g.send('hello') print(ret)我们注意到,在上面这段代码中,我们使用了send方法,而且不同于之前的yield形式,我们对yield进行了一个赋值, 注意:# send和__next__功能相似,send在获取下一个值的时候,将上一个yield位置赋值给一个变量 使用send的两个注意点: 第一次使用生成器函数时,必须先使用__next__函数,因为send要获取上一个yield位置,经过验证,可以用send(None)来代替 # 最后一个yield不接受外部的值 1)一个移动平均值的例子:
def average(): ave = 0 sum = 0 count = 0 while True: num = yield ave sum += num count += 1 ave = sum/count g = average() g.__next__() ave1 = g.send(10) ave1 = g.send(20) print(ave1)这个代码虽然很简单,但是在while中,num = yield ave这条语句的位置很重要,如果这条语句放在循环的最后一句,执行就会出现错误,因为num没有被定义
2)我们也可以把g.__next__这条语句放在装饰器中
def init(func): def inner(*args, **kwargs): ret1 = func(*args, **kwargs) ret1.__next__() return ret1 return inner @init#init = init(average) def average(): ave = 0 sum = 0 count = 0 while True: num = yield ave sum += num count += 1 ave = sum/count g = average() ave1 = g.send(10) ave1 = g.send(20) print(ave1)这段代码相当于装饰器和生成器进阶的融合,一定要好好理解
0)在python2中,如果要逐个获取字符串中每一个变量,只能使用如下方式
def geration(): a = '67890' b = '12345' for i in a: yield I for i in b: yield i g = geration() for i in g: print(i)1) 在python3中,更新了yield from语句,它的使用如下;
def geration(): a = '67890' b = '12345' yield from a yield from b g = geration() for i in g: print(i)两种方法的结果都是一样的,所以在今后的学习中,遇到了yield from语句,一定不要惊讶
0)列表推导式
egg_list = ['鸡蛋%d' % i for i in range(10)] print(egg_list)1)生成器表达式
egg_list1 = ('鸡蛋%d' % i for i in range(10)) print(egg_list1) for i in egg_list1: print(i)生成器表达式返回的永远是一个生成器,只有对生成器中的每一个元素进行输出,才能达到我们想要的效果
两者的区别:
0)列表推导式使用的是[] 而生成器表达式使用的是() 1) 与列表推导式相比较,生成器表达式几乎不占用内存 因为它一次取一个,而列表推导式一次全部都取出来了 2)生成器表达式返回的是一个生成器,这也是最关键的一点
0)30以内能被3整除的数的平方
egg_list = [i*i for i in range(30) if i % 3 == 0] print(egg_list)注意:一个完整的列表推导式要有if,if的作用相当去筛选,没有if相当于遍历 1)嵌套列表中,e出现2次的名字 先for大列表再for小列表
0)将字典的key和value对调
mask = {'a': 10, 'b': 20} antimask = {mask[k]: k for k in mask} print(antimask)这里简单介绍一下什么是可哈希的,什么是不可哈希的? 不严谨但易懂的解释: 一个对象在其生命周期内,如果保持不变,就是hashable(可哈希的)。 hashable ≈ imutable 可哈希 ≈ 不可变 在Python中: list、set和dictionary 都是可改变的,比如可以通过list.append(),set.remove(),dict[‘key’] = value对其进行修改,所以它们都是不可哈希的; 而tuple和string是不可变的,只可以做复制或者切片等操作,所以它们就是可哈希的。 总结一下,可哈希的数据类型有元组,字符串,以及字典的键
0)寻找集合中每一个元素的平方
suqrat = {i*i for i in [1, -1, 2]} print(suqrat)注意:集合本身具有去重功能
为什么没有元组推导式呢?其实这是一个很有趣的问题!!! 我在最后会公布答案
直接赋值:相当于给原对象新贴了一个标签
a = 1 b = a print('a:', id(a)) print('b:', id(b)) 浅拷贝:a 和 b 是独立的对象,但他们的子对象还是指向统一对象(是引用)。 使用浅拷贝和深拷贝时,需要引入copy模块,即要在开始前加上import copy
import copy a = [1, {1, 2, 3}] b = copy.copy(a) a.append(2) print(a, b) a[1].add(4) print(a, b) 深拷贝:a和b是独立的对象,他们的子对象也是独立的对象
import copy a = [1, {1, 2, 3}] b = copy.deepcopy(a) a.append(2) print(a, b) a[1].add(4) print(a, b) 有人把直接赋值比喻成:旧瓶装旧酒;浅拷贝比喻成:新瓶装旧酒;深拷贝比喻成:新瓶装新酒,我觉得很有意思
知道为什么没有元组推导式吗? 因为 因为元组的符号是(),这个符号已经被生成器表达式用了,哈哈哈!