今天是三次课放在一起写,主要是这三次课互相连接比较紧密,而且涉及到一些之前我们学过的内容,第一节Mutable Values讲述了一些小的概念如nondestructive&destructive,第二节主要讲述了python list的一些方法和作用域等问题;
Mutable Values : https://inst.eecs.berkeley.edu/~cs61a/sp21/assets/slides/13-Mutable_Values_full.pdf
List mutation + Identity + Nonlocal:
https://inst.eecs.berkeley.edu/~cs61a/sp21/assets/slides/14-List_mutation_+_Identity_+_Nonlocal_full.pdf
Iterators + Generators:
https://inst.eecs.berkeley.edu/~cs61a/sp21/assets/slides/15-Iterators_+_Generators_full.pdf
一、lecture 1.1 Mutable Values首先先引出non-destructive和destructive的概念。我们该怎么理解这个内容呢,简单来说,就是一个 *** 作是否对原来的对象有影响,看一下这个定义。
图1 : non-destructive和destructive的示例
这样的定义可能有些抽象,就是说有一个对象,如果经过一个 *** 作后其本身没有发生变化,就是非破坏性的;如果经过 *** 作后产生了一个新的对象,并且引用A结果返回的是新对象且和A不同,则称这个 *** 作是破坏性的;接下来看几个例子:
图2 : 例1
为什么这是非破坏性的,因为虽然其返回的是一个新树,所有节点的值都扩大了两倍,但是对于原来的树t,并没有发生变化;
若修改一下写法,那么这种 *** 作就可以是破坏性的(只是一种思路,具体实现没有验证过):
def double(t):
for tt in child(t):
tt[1] *= 2
return t
然后引入Immutability or Mutability的概念,这个在之前我们说基础的数据结构中也提到过,这里也就不过多说明;
Immutability: An immutable value is unchanging once created. Immutable types (that we've covered): int, float, string, tuple
Mutability: A mutable value can change in value throughout the course of computation. All names that refer to the same object are afected by a mutation.Mutable types (that we've covered): list, dict
1.2 list这一小节我们就来学习一下list的基本 *** 作,主要包括切片、列表复制和列表方法等;
图3 : 列表的切片 *** 作
对于列表的切片 *** 作我觉得可以多上手熟练一下,基础的语法就如同上图所示,为什么说这里的 *** 作时破坏性的呢?很明显,所有的 *** 作都更改了最开始L列表;
参考资料:Python List Slicing - GeeksforGeeks
然后是列表的一些常用的方法,为了便于后续的查阅我将其整理了表格。
方法名 | 作用 | 注意事项 | |
list.append() | adds a single element to a list | 可以是单个元素或列表 | destructive |
list.extend() | adds all the elements in one list to a list | 不可以是单个不可迭代元素如int | destructive |
list.pop() | removes and returns the last element | destructive | |
list.remove() | removes the first element equal to the argument | 注意只删除列表中第一个出现的要删除元素 | destructive |
list.index() | 返回第一个要查询元素的下标 | non-destructive | |
list.sort() | 对列表内元素进行排序 | 可以通过设置关键字定义排序 | destructive |
这里提到的都是我平时使用的和课程上提到的常用的list方法,但是限于篇幅等未能展开演示一些细节,可以在平时的使用中多理解体会一下。
具体的使用参照资料:Python 列表(List) | 菜鸟教程
1.3 Scopes这一小节就是简答的作用域的概念,由于python中没有定义数据类型的相关概念,所以对于全局作用域和局部作用域的联系有时需要特殊说明,我们先来看一下下面这种情况:
图4 : 作用域例题
首先对于第一种情况, 我们只是向一个列表中添加元素,但是并没有改变这个对象所指向的内存,即这里要区分=赋值语句和这种情况的区别,这是两种不一样的方法,虽然有些时候这些 *** 作的结果是一样的;第二种情况则是我们需要改变全局的current,这种是不被允许的,即函数不能重新赋值全局变量;并且第二种情况是函数先判断了定义了一个局部current变量,但是未经过赋值却进行了计算,这也是不允许的;
修改的方法是可以在函数中添加一个global current,表示我们可以对全局变量进行修改,但是这是不推荐的,因为会带来一些麻烦,所以尽量避免使用global关键字;
然后是global和nonlocal的使用规则如下:
图5: 访问绑定在全局范围内的变量是可以的,但是重新分配全局对象是不行的,除非添加global
图6 :总的规则
1.4 Iterators + Generators本节内容比较重要,主要是迭代器和生成器的内容,首先我们要记住python中可迭代的对象有list、tuple、dictionary、set等结构。那么这些结构的特点就是可以通过构建迭代器iterator来访问其中的对象,接下来我们将构建一个简单的迭代来演示一下。
list_a = [1, 2, 3, 4]
iter_a = iter(list_a)
next(iter_a)#1
next(iter_a)#2
next(iter_a)#3
next(iter_a)#4
next(iter_a)#StopIteration
这是非常简单的关于建立list迭代器的语法,同时为了避免最后异常的抛出,我们可以回顾一下之前学习到的异常情况,改善一下上述的写法:
list_a = [1, 2, 3, 4]
iter_a = iter(list_a)
try:
while True:
print(next(iter_a))
except:
print("已经遍历完全部对象")
然后我们就有一个问题,我们为什么要使用迭代器这种写法呢?因为在python的底层语法中,我们就是利用迭代器的思想,对可迭代对象做循环遍历的,而其语法上和效率上对循环进行了优化,所以采用循环的方式会比直接使用迭代器的方式执行效率更高,但是根本上还是基于循环的方式写成的。
图7: python for循环的执行思路
然后下一页ppt给我们一种对于不同对象重新设计iter和next方法的思路,我们可以在创造一个特殊的类时重写这两个方法达到使对象可迭代。
图8 : 对象的__iter__()和__next__()方法
大概讲完了迭代器,我们再来学习一下生成器generator。(说明:课程内容都是讲授一个大概的语法,具体的使用理解还需要搭配作业和实验来巩固)
我们先来看generator的定义:
图9: generator定义
generator使用yield关键字替代了return返回对象,其本质上也是一个迭代器。那么他是怎么工作的呢?首先当调用函数时,python立刻绑定了一个迭代器对象;其次每次调用一次next(),他就会执行一次函数主体并在yield语句停止并返回当前yield语句的值。这样解释有点难理解,我们引入一个例子看一下。
def func():
num = 0
while(num <= 10):
yield num
num += 2
图10 : 简单的生成器演示
那么我们为什么要使用生成器呢?简单来说就是使用生成器可以节省大量内存空间。因为使用生成器只会在使用next()方法时,才会返回值。这样就节省了大量的空间,尤其是有时候程序中一个容器中存在很多元素的情况下,比如说使用普通的返回,函数返回了一个包含3000个元素的列表,而使用生成器的情况下,只要每次你调用next()方法,才会返回一个对象,这在一些情况下确实非常节省内存。
以上则是本次的三次lec的内容,接下来的这些作业我会抓紧完成贴在后面
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)