python学习生涯 day3-5

0 global和nonlocal关键字

python学习生涯 day3-5_第1张图片

注意: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)!

python学习生涯 day3-5_第2张图片

x = -1


def outer():
    x = 0

    def inner():
        global x
        x = 1
        print('inner:', x)

    inner()
    print('outer:', x)


outer()
print('global:', x)

python学习生涯 day3-5_第3张图片

1 可迭代协议和迭代器协议

可迭代协议:凡是含有__iter__方法的都是可迭代的,(在python中,可迭代的数据类型有字符串,列表,元组,字典,集合,枚举,range等,int和布尔类型不包含__iter__方法)
迭代器协议:凡是含有__iter__和__next__方法的都是迭代器
For循环的实质就是在调用__next__方法
l = [1, 2, ‘8hjg’, 0]
for i in l:
pass
‘’’

for循环的实质为:

  1. iterator = l.iter()#可迭代的数据类型使用__iter__方法后,就返回了一个迭代器
  2. iterator.next()#对于迭代器使用__next__方法后,相当于从迭代器中一个一个取值
  3. 当出现false后,执行结束
    ‘’’
    迭代器的优势:
    a)迭代器中的值会一个一个的被取出
    b)节省内存空间,循环一次产生一个变量,而不是一次性全部产生
    尤其是对于文件句柄和range()而言,特别节省内存空间

2生成器

生成器的本质还是迭代器,凡是含有yield关键字的函数都是生成器函数
0)yield不能与return混用,且必须写在函数内部
1)生成器函数在被执行后会返回一个生成器作为返回值 ,而函数内部的语句并不会被执行
2)一个简单的生成器函数的例子

python学习生涯 day3-5_第4张图片
打印结果如下图:
python学习生涯 day3-5_第5张图片
简单介绍一下return和yield的区别吧:
当一个程序执行到return时,程序结束,return之后的语句不会再执行,换句话说,return就是程序结束的标志,而yield不会引起程序结束,当程序运行到yield时,相当于暂停了,如果下一次又调用函数,需要执行yield时,接着上次暂停的地方继续执行(不知道这么理解对不对)
我个人喜欢用一些生活中的例子来进行类比:
执行程序相当于在跑马拉松,当出现return时,相当于选手退出比赛,当出现yield时,相当于选手暂停休息了一会,当生成器函数被再次调用时,选手又接着上次暂停的地方继续开始跑,直到出现下一个yield
说到跑步的例子,我就不得不再说一下break和continue的区别了:
这也不能说是我思维混乱,只能说思维比较跳跃吧
由于break和continue一般是在循环中使用的,所以类比成在操场跑圈吧
break相当于选手直接退赛,不再参与跑圈这一运动,而continue相当于选手放弃了这圈中没有跑完的这一部分,直接从下一圈的起点开始跑

3 生成器进阶知识

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)

python学习生涯 day3-5_第6张图片

我们注意到,在上面这段代码中,我们使用了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)

这段代码相当于装饰器和生成器进阶的融合,一定要好好理解

4yield from 语句

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语句,一定不要惊讶

python学习生涯 day3-5_第7张图片

5 列表推导式和生成器表达式

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)

python学习生涯 day3-5_第8张图片
生成器表达式返回的永远是一个生成器,只有对生成器中的每一个元素进行输出,才能达到我们想要的效果

两者的区别:

0)列表推导式使用的是[] 而生成器表达式使用的是()
1) 与列表推导式相比较,生成器表达式几乎不占用内存
因为它一次取一个,而列表推导式一次全部都取出来了
2)生成器表达式返回的是一个生成器,这也是最关键的一点

6 各种列表推导式

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次的名字python学习生涯 day3-5_第9张图片
先for大列表再for小列表在这里插入图片描述

7 字典推导式

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是不可变的,只可以做复制或者切片等操作,所以它们就是可哈希的。
总结一下,可哈希的数据类型有元组,字符串,以及字典的键

8 集合推导式

0)寻找集合中每一个元素的平方

suqrat = {
     i*i for i in [1, -1, 2]}
print(suqrat)

在这里插入图片描述

注意:集合本身具有去重功能

为什么没有元组推导式呢?其实这是一个很有趣的问题!!!
我在最后会公布答案

9 直接赋值,浅拷贝和深拷贝

直接赋值:相当于给原对象新贴了一个标签

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)

在这里插入图片描述


有人把直接赋值比喻成:旧瓶装旧酒;浅拷贝比喻成:新瓶装旧酒;深拷贝比喻成:新瓶装新酒,我觉得很有意思

知道为什么没有元组推导式吗?
因为
因为元组的符号是(),这个符号已经被生成器表达式用了,哈哈哈!

你可能感兴趣的:(python学习生涯 day3-5)