python课程学习第三课

时间:2022-01-09 22:56:49

容器

  • list列表
  • 列表是python中最基本的数据结构。序列中的每一个元素都分配一个数字-它的位置 ,或索引。第一个索引是0,第二个索引是1,以此类推
  • 列表的数据项不需要具有相同的类型
  • tuple 元组(只读列表)
  • dict字典
  • 字典中的每个键值(key=>value)对用冒号:分开,每个对之间用逗号(,)分开整个字典包含在花括号{}里
  • set集合
  • 是一个无序不重复元素集,基本功能包括功能测试和消除重复元素。集合对象还支持 union(联合),intersection(交),difference(差)和sysmmetric difference (对称差集)等数学运算

    list/tuple的基本操作 :可以为任意元素

  • 创建

  • 添加元素(list only):append,extend
  • 删除元素(list only):del,pop
  • 根据索引读写(tuple只读)
  • 判断容器是否为空
  • 字符串转换
  • 容器元素数量
  • 遍历
#平方表
li=[]
for i in range(10000):
li.append(i*i)
for i in range(10):
print(li[i])
generator=(x*x for x in range(10000))
print(type(generator))
for i in range(10):
print(next(generator))
#for i in generator:
# print(i)
print(type(range(10)))#在py3中为range类型,使用生成器

#菲波拉契数
def fib(limit):
n,a,b=0,0,1
while n<limit:
yield b
a,b=b,a+b
n+=1
# return 'done'
import traceback
f=fib(5)
print(type(f))
print(next(f))
print(next(f))
print(next(f))
print(next(f))
print(next(f))
try:
print(next(f))
except StopIteration:
traceback.print_exc()
for i in fib(5):
print(i)
0
1
4
9
16
25
36
49
64
81
<type 'generator'>
0
1
4
9
16
25
36
49
64
81
<type 'list'>
<type 'generator'>
1
1
2
3
5
1
1
2
3
5
Traceback (most recent call last):
File "<ipython-input-62-2cb80b5beb80>", line 32, in <module>
print(next(f))
StopIteration

迭代器

  • 问题的提出
    • 可以直接作用于for循环的对象统称为迭代对象:Iterable(可迭代的)
    • 可以被next()(哪怕是自己定义一个next,通过调用next访问下一个值也被称为迭代器)函数调用并不断返回下一个值得对象统称为迭代器(迭代器):Iterator(表示一个惰性计算的序列)
  • 集合数据类型 如list,dict,str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象
str='hello'
for i in str:
print(i)
#str.next()
ss=iter(str)
ss.next()


from collections import Iterable
from collections import Iterator
print(isinstance([1,2,3],Iterable))#isinstance判断继承关系
print(isinstance({},Iterable))
print(isinstance(123,Iterable))
print(isinstance('hello',Iterable))

print(isinstance([1,2,3],Iterator))

g=(x*x for x in range(3))
print(type(g))#生成器的目的是惰性计算,迭代器的作用是惰性计算,所以生成器是迭代器。for(可迭代)+惰性计算=迭代器
print(isinstance(g,Iterable))
print(isinstance(g,Iterator))
for i in g:#因为是Iterable,所以可以用for访问
print i
class fin(object):
def __init__(self,max):
self.max=max
self.n,self.a,self.b=0,0,1
def __iter__(self):
return self
def next(self):
if self.n<self.max:
r=self.b
self.a,self.b=self.b,self.a+self.b
self.n+=1
return r
raise StopIteration()
f=fin(5)
print(isinstance(f,Iterable))
print(isinstance(f,Iterator))
h
e
l
l
o
h


True
True
False
True
False
<type 'generator'>
True
True
0
1
4
True
True

惰性求值与惰性序列
迭代器的一个优点就是它不需要你事先真备好迭代过程中的所有的元素。
迭代器仅仅在迭代至某个元素时才计算该元素,而在这之前或之后,元素可以不存在或销毁。这个特点使的它特别适合一些巨大的或是无限的集合,比如几个G的文件或是斐波拉契数列等等。
这个特点被称为延迟计算或惰性求值

迭代器:迭代器是访问元素的一种方式。迭代器对象从集合的第一个元素开始访问,知道所有的元素访问结束。迭代器只能往前不会后退,不过这也没什么,因为在迭代的过程中很少有人要后退。
使用迭代器的优点:对于原生支持随机访问的数据结构(如list,tuple),迭代器与经典的for循环并无明显的优势,反而丢失了索引(可以使用内建函数enumerate()找回这个索引值)。但对于无法随机访问的数据结构(如set)而言,迭代器是唯一访问元素的方式。
另外,迭代器的另一大特点是不需要提前准备好整个迭代过程中的所有的元素。迭代器仅仅在迭代到某个元素时才计算该元素,而在这之前或之后,元素可以不存在或销毁。这个特点使得它特别适合用于遍历一些巨大或是无限的集合,比如几个G的文件,或是斐波拉契数列。
迭代器更大的功劳是提供一个统一访问集合的接口,只要定义了iter()方法对象,就可以使用迭代器访问。
迭代器有两个基本的方法:

  • next方法:返回迭代器的下一个元素
  • iter:返回迭代器本身
    下面用生成斐波拉契数列为例子,说明为什么用迭代器。
    代码一
def fin(max):
n,a,b=0,0,1
while n<max:
print(b)
a,b=b,a+b
n+=1
fin(5)

直接在函数中使用print会导致函数的复用性变差,因为fin返回None,其他函数无法获得fin返回的数列
代码二

del fin(max):
li=[]
n,a,b=0,0,1
while n<max:
a,b=b,a+b
li.append(b)
return liprin

满足了可复用的要求,但是占用了内存空间,最好不用‘’
对比

for i in range(1000):
pass
for i in xrange(1000):
pass

前一个返回1000个元素的列表,后一个用迭代的方式生成下一个元素。因此可用迭代器的方式来解决复用可占空间的问题
代码三

class fin(object):
def __init__(self,max):
self.max=max
self.n,self.a,self.b=0,0,1
def __iter__(self):
return self
def next(self):
if self.n<self.max:
r=self.b
self.a,self.b=self.b,self.a+self.b
self.n+=1
return r
raise StopIteration()
for i in fin(5):
print i

fib类通过next()不断返回数列的下一个数,内存占用始终为常数
使用迭代器
使用内建的工厂函数iter(iterable)可以获得迭代器对象:

li=xrange(5)
lii=iter(li)
lii
<rangeiterator at 0x4bb82b0>

使用next可以访问下一个元素

lii.next()

python处理迭代器越界是抛出StopIteration异常
了解了StopIteration可以使用迭代器进行遍历

li=xrange(5)
lii=iter(li)
try:
while True:
x=lii.next()
print(x)
except StopIteration:
pass
0
1
2
3
4

事实上因为迭代器如此普遍,Python专门为for关键字做了迭代器的语法糖。在for循环中Python将自动调用工厂函数iter()获得迭代器,自动调用next()获取元素,换完成了检查StopIteration的工作

a=[0,1,2,3,4,5]
for x in a:
print(x)
0
1
2
3
4
5

首先Python对in后的关键字后的对象调用iter函数迭代器,然后调用next方法获得元素,知道抛出StopIteration异常

定义迭代器(以斐波拉契数列为例)

class fin(object):
def __init__(self,max):
self.max=max
self.n,self.a,self.b=0,0,1
def __iter__(self):
return self
def next(self):
if self.n<self.max:
r=self.b
self.a,self.b=self.b,self.a+self.b
self.n+=1
return r
raise StopIteration()
for i in fin(5):
print i

生成器
带有yield的函数在Python中称为generator(生成器),还以斐波拉契数列为例,生成器(yield)既可以保持代码一的简洁性,又可达到代码三的效果

def fib(max):
n,a,b=0,0,1
while n<max:
yield b
a,b=b,a+b
n+=1
for i in fib(5):
print(i)
1
1
2
3
5

简单的讲,yield的作用就是将一函数变成generator,带有yield的函数不再是一个普通的函数,Python解释器将其视为一个generator,调用fib(5)不会执行fib函数,,而是返回一个iterable对象!在for循环执行时,每次循环都会执行函数内部的代码,执行到yield b时,fib返回一个迭代值,下次迭代时,代码从yield b的下一条语句开始执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到yield。看起来就好像是一个函数在正常的执行过程中被yield中断了数次,每次中断都会通过yield返回当前的迭代值。
也可以手动调用fib(5)的next()方法(因为fib(5)是一个generator对象,该对象具有next()方法),这样我们可以更清楚的看到fib的执行流程:

f=fib(5)
f.next()
f.next()
1
1
ff.next()
StopIteration                             Traceback (most recent call last)
<ipython-input-45-3442c82fde64> in <module>()
----> 1 ff.next()

StopIteration:

return作用
在一个生成器中,如果没有return,则默认执行到函数完毕;如果在执行过程中遇到return,则抛出StopIteration终止迭代
文件读取

def file_read(fpath):
BLOCK_SIZE=1024
with open(fpath,'rb') as f:
while True:
block=f.read(BLOCK_SIZE)
if block:
yield block
else :
return
for x in file_read('D:\mycookie.txt'):
print(x)
hahahh ,hello world!
我是张策

如果直接对文件对象调用read()方法,会导致不可预测的内存占用。好的方法是利用固定长度的缓冲区不断读取文件内容。通过yield我们不再需要编写读文件的迭代类,就可以轻松实现文件的读取。
语法糖
语法糖意指没有个计算机语言添加新功能,而只是对人类来说更“甜蜜”的语法。语法糖往往给程序员提供更实用的编码方式,有益于更好地变成风格,更易读。不过其实并没有给语言添加新的东西。
举个例子:在c语言里用a[i]表示(a+i),a[i][j]=(*(a+i)+j).
语法糖可以给我们带来方便,,是一种便捷的写法,编译器会帮我们做转换,而且可以提高开发编程的效率,在性能上也不会带来损失。

作业

  • 螺旋矩阵;给定一个m*n要素的。按照螺旋顺序,返回该矩阵的所有要素。
    [[1,2,3],
    [4,5,6],
    [7,8,9]]
    应该返回[1,2,3,6,9,8,7,4,5]

  • 用栈(使用list)实现队列:支持push(element)pop()和top()方法都应该返回第一个元素的值。比如执行以下操作序列:push(1),pop(),push(2),push(3),top(),pop()你应该返回1,2,2。

  • 矩阵转换:给定矩阵A,令矩阵B里每个元素B[i][j]的值等于A[0][0]到A[i][j]子矩阵元素之和
  • 反转单向链表
class ListNode:
def __init__(self,val,next=None):
self.val=val
self.next=next