流程python学习笔记:第二章

第二章开始介绍了列表这种数据结构,这个在python是经常用到的结构

列表的推导,将一个字符串编程一个列表,有下面的2种方法。其中第二种方法更简洁。可读性也比第一种要好

str=‘abc‘
string=[]
for s in str:
    print string.append(s)
ret=[s for s in str]
print ret

用这种for…in的方法来推导列表,有个好处就是不会有变量泄露也就是越界的问题。这在c语言中是需要特别注意的问题。

对于两个以上的列表推导作者用到了笛卡尔积的例子。笛卡尔又称为直积。可以理解为两个集合的全排列。依然是两种表达式。

colors=[‘black‘,‘white‘]
sizes=[‘s‘,‘m‘,‘l‘]
tshirts=[(color,size) for color in colors for size in sizes]
print tshirts
for color in colors:
    for size in sizes:
        print (color,size)
 
列表的推导还有一种方式,称为生成式表达式。表达式都差不多,不过是方括号编程了圆括号而已:生成器的好处是什么呢。列表推导是首先生成一个6个组合的列表。这会占用到内存。而生成式则是在每一个for循环运行时才生成一个组合。这样就不会预先占用内存。
colors=[‘black‘,‘while‘]
sizes=[‘s‘,‘m‘,‘l‘]
for tshirt in (‘%s,%s‘ % (c,s) for c in colors for s in sizes):
    print tshirt
 这个数据比较小,还无法看出来,举下面的例子。有10000个数组的列表。分别用列表推导法和生成式表达法来进行遍历。并用memory_profiler来监控代码占用的内存
from memory_profiler import profile
@profile
def fun_try():
    test=[]
    for i in range(10000):
        test.append(i)
    for num in (t for t in test):
        print num
首先是生成式的结果,可以看到在for num in (t for t in test)中没有内存增加

改成列表推导后的结果。可以看到[t for t in test]增加了0.1M的内存。

下面介绍下元组。说到元组,第一个反应就应该是不可变列表。但作者同时还介绍了元组的很多其他特性。首先来看下元组的拆包
如果有一个元组(33.9,118)分别表示经纬度。需要对经纬度进行分别赋值。代码如下。这就是元组拆包的运用。这个实现原理其实就是将可迭代对象里的元素一并赋值到对应的变量组成的元组中。
location=(33.9,118)
latitude,longitude=location
print latitude,longitude
 
如果在进行拆包的同时,并不是对所有的元组数据都感兴趣。_占位符就能帮助处理这种情况。像下面的代码。分隔符为/。第一个应该是abc,第二个是def。我们只需要第二个元素,因此用_,second=str.split(‘/‘),这样就只会得到第二个元素。
str=‘abc/def‘
_,second=str.split(‘/‘)
print second
 
上面元组拆包的时候是对可迭代对象进行遍历,然后一一赋值到变量。但是如果想通过给每个元组元素的命名来访问,则需用到命名元组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+=str
str1+=str1
print "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=[[‘_‘]*3 for i in range(3)]
print board
board[1][2]=‘x‘
print board
首先建立了3个全为_的列表,然后对第二个列表的第3个元素进行赋值。结果如下
[[‘_‘, ‘_‘, ‘_‘], [‘_‘, ‘_‘, ‘_‘], [‘_‘, ‘_‘, ‘_‘]]
[[‘_‘, ‘_‘, ‘_‘], [‘_‘, ‘_‘, ‘x‘], [‘_‘, ‘_‘, ‘_‘]]
输出3个列表的ID
print id(board[0])
print id(board[1])
print id(board[2])
分别属于不同的ID。
38919784
38090472
38090752
我们再来看下另外一种用法。下面的代码对一个包含3个列表的列表进行*3的操作。
board=[[‘_‘]*3]*3
print board
board[1][2]=‘x‘
print board
得到的结果如下:可以看到首先也是生成了3个列表,但是赋值的时候,所有列表的第3个元素都被赋值了。
[[‘_‘, ‘_‘, ‘_‘], [‘_‘, ‘_‘, ‘_‘], [‘_‘, ‘_‘, ‘_‘]]
[[‘_‘, ‘_‘, ‘x‘], [‘_‘, ‘_‘, ‘x‘], [‘_‘, ‘_‘, ‘x‘]]
我们继续看下对象的ID
print id(board[0])
print id(board[1])
print id(board[2])
发现id都一样。说明了全部指向的是同一个对象。
39122904
39122904
39122904
这种实现的方法和下面的一样,相当于首先初始化好row,然后在board中对同一个row进行插入。
row=[‘_‘]*3
board=[]
for i in range(3):
    board.append(row)
 
而board=[[‘_‘]*3 for i in range(3)]的操作类似如下,在每次迭代的对象中,都新生成一个row。然后将不同的row插入到board中去。
board=[]
for i in range(3):
    row=[‘_‘]*3
    board.append(row)
 
如果对不可变对象中的可变对象进行赋值会产生什么后果,比如下面的这段代码
t=(1,2,[30,40])
t[2]+=[50,60]
print t
是直接抛出异常呢还是赋值成功。 我们在http://www.pythontutor.com对这个代码可视化看下。首先第一步初始化t之后,得到的结果如下,t中包含一个元组。其中第3个元组元素指向一个列表

第二步对元组中的列表进行赋值后,可以看到下图中列表的元素被更新了,但同时也报了异常。

我们将代码修改成如下再执行下
t=(1,2,[30,40])
t[2].append([50,60])
print t
第一步都一样,我们从第二步开始查看。可以看到这个时候没有抛出异常,从结构来看是在[30,40]这个列表后面继续插入了一个列表。

最终得到的结果是(1, 2, [30, 40, [50, 60]])
为什么这两种实现会带来不同的结果呢。原因在于t是一个元组属于不可变对象。但用t[2]+=[50,60]的时候是对一个元组进行赋值。所以报错误。但是同时t[2]又属于一个列表可变对象。因此数据也更新成功了
但是如果用t[2].append([50,60])的操作则是对一个列表进行操作,而并没有对一个元组进行赋值。因此能够更新成功且不会报错误。这是一个很有趣的例子。对于理解可变对象和不可变对象的操作很有帮助
数组:
在列表和元组中,存放的是具体的对象,如整数对象,字符对象。如下面的整数b。占据12个字节。因为存放的是整数对象,而非整数本身
b=1
print sys.getsizeof(b)
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter2.py
12
对于存放大量数据来说。我们选择用数组的形式要好很多。因为数组存储的不是对象,而是数字的机器翻译。也就是字节表述。和C语言中的数组是一个道理

在array中需要规定各个字符的类型,如上表中的Type code。定义好类型后则数组内的元素必须全是这个类型,否则会报错。就像下面的代码。类型规定的是b也就是单字节的整数。但是在插入的时候却是字符,则报错:TypeError: an integer is required
num=array(‘b‘)
num.append(‘c‘)
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter2.py
Traceback (most recent call last):
  File "E:/py_prj/fluent_python/chapter2.py", line 18, in <module>
    num.append(‘c‘)
TypeError: an integer is required
在上表中,每个类型都有字节大小的限制。如果超出了字节大小的限制也是会报错的。还是b的这个类型,是有符号的单字节整数,那么范围是-128到127.如果我们插入128.则报错:signed char is greater than maximum 提示超过了最大
num=array(‘b‘)
num.append(128)
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter2.py
Traceback (most recent call last):
  File "E:/py_prj/fluent_python/chapter2.py", line 18, in <module>
    num.append(128)
OverflowError: signed char is greater than maximum
 
对于数组这种结构体来说,由于占用的内存小,因此在读取和写入文件的时候的速度更快,相比于列表来说的话。下面来对比下:
首先是用列表生成并写入txt文档的用法
def arry_try_list():
    floats=[float for float in range(10**7)]
    fp=open(‘list.txt‘,‘wb‘)
    start=time.clock()
    for f in floats:
        fp.write(str(f))
    fp.close()
    end=time.clock()
    print end-start
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter2.py
7.83854664189
总共耗时了7秒
 
再来看数组的形式:
def array_try():
    floats=array(‘d‘,(random() for i in range(10**7)))
    start=time.clock()
    fp=open(‘floats.bin‘,‘wb‘)
    floats.tofile(fp)
    fp.close()
    end=time.clock()
    print end-start
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter2.py
0.883260677006
耗时0.88秒。速度提高了9倍
 
再来对比下写入的速度:
def array_try():
    floats=array(‘d‘)
    start=time.clock()
    fp=open(‘floats.bin‘,‘rb‘)
    floats.fromfile(fp,10**7)
    fp.close()
    end=time.clock()
    print end-start
E:\python2.7.11\python.exe E:/py_prj/fluent_python/chapter2.py
0.0515373089318
数组的方式只用了0.05秒,也是非常的快
 
双向队列
在列表或者数组中,可以用append,pop来模拟栈或者队列的操作。本章作者介绍了双向队列,在进行数组的移动时更为高效
dq=deque(range(10),maxlen=10)
print dq
deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
dq.rotate(3)  #将最右边的3个元素移到左边
print dq
deque([7, 8, 9, 0, 1, 2, 3, 4, 5, 6], maxlen=10)
dq.rotate(-3) #将最左边的3个元素移到右边
print dq
deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
num=dq.popleft()   #最左边的元素移除
print dq
deque([1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
dq.appendleft(num) #将元素插入到最左边
print dq
deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)


				
时间: 2024-10-08 02:51:11

流程python学习笔记:第二章的相关文章

Mysql Cookbook学习笔记第二章

1,使用python链接mysql + ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # -*- coding: utf-8 -*- # connect.py --连接到MySQL服务器 import sys import MySQLdb try:     conn = MySQLdb.connect(db = "cookbook",                            host = "localhost"

Python学习笔记__9章 IO编程

# 这是学习廖雪峰老师python教程的学习笔记 1.概览 IO在计算机中指Input/Output,也就是输入和输出. 由于程序和运行时数据是在内存中驻留,由CPU这个超快的计算核心来执行,所以涉及到数据交换的地方,通常是磁盘.网络等,这些地方就需要IO接口. 数据从内存往外发是Output 数据从外往内存发是Iutput 2.同步IO和异步IO 同步IO:发起IO请求后,等到IO的返回结果,在接着往下执行 异步IO:发起IO请求后,可以去做其他事.IO结果返回后,会通知调用者. 注:本章所讲

Python学习笔记__17章电子邮件

# 这是学习廖雪峰老师python教程的学习笔记 Email的历史比Web还要久远,直到现在,Email也是互联网上应用非常广泛的服务.而且几乎所有的编程语言都支持发送和接收电子邮件. 1.邮件的原理 1.1.邮件的发送流程 假设我们要把邮件从[email protected] 发送到 @sina.com MUA:Outlook或者Foxmail这类编写/发送/邮件的软件,被称为MUA[Mail User Agent--邮件用户代理] MTA:Email服务提供商,比如网易.新浪等等就是MTA[

Python学习笔记__5章 模块

# 这是学习廖雪峰老师python教程的学习笔记 1.概览 为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,在Python中,一个.py文件就称之为一个模块(Module) 1.1.使用模块的好处 提高了代码的可维护性 当一个模块编写完毕,就可以被其他地方引用 避免函数名和变量名冲突.相同名字的函数和变量可以分别存在不同的模块中 1.2.python组织模块的方法 为了避免模块名冲突,Python又引入了按目录来组织模块的方法,称为包(Pack

Python学习笔记__14章 virtualenv

# 这是学习廖雪峰老师python教程的学习笔记 当多个应用所需环境不同,可用virtualenv来为每一个应用创建一套"隔离"的Python运行环境 1.安装 $ pip3 install virtualenv 2.假定我们要开发一个新的项目,需要一套独立的Python运行环境 1)创建目录 mkdir myproject cd myproject/ 2)创建一个独立的Python运行环境,命名为venv virtualenv --no-site-packages venv # vi

【PMP】Head First PMP 学习笔记 第二章 组织、约束和项目

第二章 组织.约束和项目 如果你希望正确地完成工作--最好有一个正确的组织. 项目联络人 项目联络人(project expediter),只是记录项目的进展情况,但是没有权利对项目做任何决策.他可能参与项目,但是并不管理任何事务. 不同类型的组织项目 职能型 项目经理的决策需要和职能经理确认 项目经理作为职能经理的助手,协助职能经理完成工作 项目经理大部分时间都用于行政任务,通常只有部分时间投入到项目管理中 在职能型组织中往往会有项目联络人 矩阵型 弱矩阵型 项目经理有一些经理有一些权利,但是

c#高级编程第七版 学习笔记 第二章 核心c#

第二章 核心C# 本章内容: 声明变量 变量的初始化和作用域 C#的预定义数据类型 在c#程序中使用条件语句.循环和跳转语句执行流 枚举 名称空间 Main()方法 基本的命令行c#编译器选项 使用System.Console执行控制台I/O 使用内部注释和文档编制功能 预处理器指令 C#编程的推荐规则和约定 2.1 第一个c#程序 2.1.1 代码 using System; namespace Wrox { Public class MyFirstClass { static void Ma

Android学习笔记—第二章 Android四大组件

第二章 Android四大组件 Activity(活动窗口): Android程序中最基本的模块,为用户操作而展示的可视化用户界面.一个Android应用程序可以只有一个Activity,也可以包含多个Activity,数量及每个Activity的作用取决于应用程序及其设计. (1)Activity的生命周期 创建→运行   onCreate   onStart   onResume 运行→销毁   onPause    onStop    onDestory 运行→停止(不可见  不可操作)

《操作系统概念》学习笔记-第二章

第二章 操作系统结构 在具有多个命令解释程序选择的系统中,解释程序被称为外壳(shell) 命令解释程序的主要作用是获取并执行用户指定的下一条命令. 有三种应用程序员常用的API,适用于windows系统的win32API,适用于POSIX系统的POSIX API,以及用于设计运行于JAVA虚拟机程序的JAVA API. 操作系统传递参数有三种方法: 最简单的方法是通过寄存器来传递参数,不过有时,参数数量会比寄存器多,这时,这些参数通常存在内存的块和表中,并将块的地址通过寄存器来传递.Linux