您的位置:首页 > 编程语言 > Python开发

流程python学习笔记:第二章(1)

2017-05-31 10:44 169 查看
第二章开始介绍了列表这种数据结构,这个在python是经常用到的结构列表的推导,将一个字符串编程一个列表,有下面的2种方法。其中第二种方法更简洁。可读性也比第一种要好
str='abc'
string=[]
forsinstr:
printstring.append(s)
ret=[sforsinstr]
printret
用这种for…in的方法来推导列表,有个好处就是不会有变量泄露也就是越界的问题。这在c语言中是需要特别注意的问题。对于两个以上的列表推导作者用到了笛卡尔积的例子。笛卡尔又称为直积。可以理解为两个集合的全排列。依然是两种表达式。
colors=['black','white']
sizes=['s','m','l']
tshirts=[(color,size)forcolorincolorsforsizeinsizes]
printtshirts
forcolorincolors:
forsizeinsizes:
print(color,size)
列表的推导还有一种方式,称为生成式表达式。表达式都差不多,不过是方括号编程了圆括号而已:生成器的好处是什么呢。列表推导是首先生成一个6个组合的列表。这会占用到内存。而生成式则是在每一个for循环运行时才生成一个组合。这样就不会预先占用内存。
colors=['black','while']sizes=['s','m','l']fortshirtin('%s,%s'%(c,s)forcincolorsforsinsizes):printtshirt
这个数据比较小,还无法看出来,举下面的例子。有10000个数组的列表。分别用列表推导法和生成式表达法来进行遍历。并用memory_profiler来监控代码占用的内存
frommemory_profilerimportprofile
@profiledeffun_try():test=[]foriinrange(10000):test.append(i)fornumin(tfortintest):printnum
首先是生成式的结果,可以看到在fornumin(tfortintest)中没有内存增加
改成列表推导后的结果。可以看到[tfortintest]增加了0.1M的内存。
下面介绍下元组。说到元组,第一个反应就应该是不可变列表。但作者同时还介绍了元组的很多其他特性。首先来看下元组的拆包
如果有一个元组(33.9,118)分别表示经纬度。需要对经纬度进行分别赋值。代码如下。这就是元组拆包的运用。这个实现原理其实就是将可迭代对象里的元素一并赋值到对应的变量组成的元组中。
location=(33.9,118)
latitude,longitude=location
printlatitude,longitude
如果在进行拆包的同时,并不是对所有的元组数据都感兴趣。_占位符就能帮助处理这种情况。像下面的代码。分隔符为/。第一个应该是abc,第二个是def。我们只需要第二个元素,因此用_,second=str.split('/'),这样就只会得到第二个元素。
str='abc/def'
_,second=str.split('/')printsecond
上面元组拆包的时候是对可迭代对象进行遍历,然后一一赋值到变量。但是如果想通过给每个元组元素的命名来访问,则需用到命名元组namdtuple。具体用法参加第一章的介绍。
序列的操作:
1增量赋值:
增量运算符+,*等其实是调用__iadd__/__add__/__mul__方法。对于可变对象来说,+调用的是__iadd__,对于不可变对象来说对象调用的是__add__。两者有什么区别呢。先看下面的例子
str=[1,2,3]str1=(1,2,3)print"str:%d"%id(str)print"str1:%d"%id(str1)str+=strstr1+=str1print"str:%d"%id(str)print"str1:%d"%id(str1)
得到的结果如下:
str:38078184
str1:24035000
str:38078184
str1:25286840
str是列表,str1是元组,列表是可变对象,元组是不可变对象。在进行加法运算后,str的id没有改变,因此还是之前的对象,但是str1的id却发生了改变,不是之前的对象了。这种的差别在于__iadd__的方法类似调用a.extend(b)的方式,是在原有的对象上进行扩展操作。但是__add__的方式类似于a=a+b。首先a+b得到一个新的的对象,然后复制给a,因此变量和之前的对象没有任何联系。而是被关联到一个新的对象。同样的乘法__imul__/__mul__也是类似的道理
列表组成的列表
board=[['_']*3foriinrange(3)]printboardboard[1][2]='x'printboard
首先建立了3个全为_的列表,然后对第二个列表的第3个元素进行赋值。结果如下
[['_','_','_'],['_','_','_'],['_','_','_']]
[['_','_','_'],['_','_','x'],['_','_','_']]
输出3个列表的ID
printid(board[0])printid(board[1])printid(board[2])
分别属于不同的ID。
38919784
38090472
38090752
我们再来看下另外一种用法。下面的代码对一个包含3个列表的列表进行*3的操作。
board=[['_']*3]*3printboardboard[1][2]='x'printboard
得到的结果如下:可以看到首先也是生成了3个列表,但是赋值的时候,所有列表的第3个元素都被赋值了。
[['_','_','_'],['_','_','_'],['_','_','_']]
[['_','_','x'],['_','_','x'],['_','_','x']]
我们继续看下对象的ID
printid(board[0])printid(board[1])printid(board[2])
发现id都一样。说明了全部指向的是同一个对象。
39122904
39122904
39122904
这种实现的方法和下面的一样,相当于首先初始化好row,然后在board中对同一个row进行插入。
row=['_']*3board=[]foriinrange(3):board.append(row)
而board=[['_']*3foriinrange(3)]的操作类似如下,在每次迭代的对象中,都新生成一个row。然后将不同的row插入到board中去。
board=[]foriinrange(3):row=['_']*3board.append(row)
如果对不可变对象中的可变对象进行赋值会产生什么后果,比如下面的这段代码
t=(1,2,[30,40])t[2]+=[50,60]printt
是直接抛出异常呢还是赋值成功。我们在http://www.pythontutor.com对这个代码可视化看下。首先第一步初始化t之后,得到的结果如下,t中包含一个元组。其中第3个元组元素指向一个列表
第二步对元组中的列表进行赋值后,可以看到下图中列表的元素被更新了,但同时也报了异常
我们将代码修改成如下再执行下
t=(1,2,[30,40])t[2].append([50,60])printt
第一步都一样,我们从第二步开始查看。可以看到这个时候没有抛出异常,从结构来看是在[30,40]这个列表后面继续插入了一个列表
最终得到的结果是(1,2,[30,40,[50,60]])
为什么这两种实现会带来不同的结果呢。原因在于t是一个元组属于不可变对象。但用t[2]+=[50,60]的时候是对一个元组进行赋值。所以报错误。但是同时t[2]又属于一个列表可变对象。因此数据也更新成功了
但是如果用t[2].append([50,60])的操作则是对一个列表进行操作,而并没有对一个元组进行赋值。因此能够更新成功且不会报错误。这是一个很有趣的例子。对于理解可变对象和不可变对象的操作很有帮助
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  流畅python