固定长度,不可变的Python对象序列
创建元组
最简单:用逗号分隔序列值更复杂:用括号将值包起来,例如:生成元素是元组的元组使用tuple函数将任意序列或迭代器转换为元组
元组中存储的对象自身是可变的,但是元组一旦创建,各个位置上的对象是无法被修改的。
In[1]:tup=tuple(['foo',[1,2],True]) In[2]:tup[2] = False # 提示错误
如果元组中的一个对象是可变的,例如列表,你可以在它内部进行修改
可以使用+号连接元组来生成更长的元组,将元组乘以整数,则会和列表一样,生成含有多份拷贝的元组.
对象自身并没有复制,只是指向它们的引用进行了复制
元组拆包
将元组型的表达式赋值给变量,Python会对等号右边的值进行拆包,即使是嵌套元组也可以拆包
In[1]:tup=(4,5,(6,7)) In[2]:a,b,(c,d)=tup In[3]:d Out[1]:7
在Python中,交换值可以采用如下方式
a,b = 1,2 b,a = a,b a #输出2 b # 输出1
拆包常用场景:遍历元组或列表组成的序列
seq = [(1,2,3),(4,5,6),(7,8,9)]
for a,b,c in seq:
print('a={0},b={1},c={2}'.format(a,b,c))
# 输出
a=1,b=2,c=3
a=4,b=5,c=6
a=7,b=8,c=9
使用*rest在函数调用时获取任意长度的位置参数列表,有时候rest部分表示要丢弃的数据,很多编程者会使用下划线(_)来表示不想要的遍历
a,b,*_ = values
元组方法
用count计量每个数值在元组中出现的次数
a = (1,2,2,2,3,4,2) a.count(2) # 4列表
长度可变,包含的内容可以修改可使用[]或者list类型函数来定义列表list函数在数据处理中常用于将迭代器或者生成器转化为列表
增加和移除元素
append方法:将元素添加到列表尾部
使用insert方法将元素插入到列表的指定位置
b_list.insert(1,'red')
insert比append计算代价更高,因为内部序列移动需要时间
想要在序列头部和尾部都插入元素,应该探索下collections.deque,它是一个双端队列,可以满足头尾部都增加的要求。
pop方法:insert的反向操作,将特定位置的元素移除并返回
b_list.pop(2)
remove方法可以移除:定位第一个符合要求的值并移除它
in关键字:查询一个值是否在列表中,not in表示不在
与字典和集合相比,检查列表是否包含一个值很缓慢,因为需要遍历,而字典和集合是基于哈希表同时查找的。
连接和联合列表
两个列表可以用+号连接extend方法向列表中添加多个元素
extend方法比用添加内容来连接列表代价更高,因为连接过程涉及创建新列表、复制对象;使用extend将元素添加到已经存在的列表是更好的方式(尤其是大型链表)。
排序
使用sort方法进行列表内部排序
二分搜索和已排序列表的维护
内建的bisect模块实现了二分搜索和已排序列表的插值
bisect.bisect会找到元素应当被插入的位置,并保持序列排序
bisect.bisect会找到元素应当被插入的位置,并保持序列排序
import bisect c = [1,2,2,2,3,4,7] bisect.bisect(c,5) # 输出6 bisect.insort(c,6) c # 输出[1, 2, 2, 2, 3, 4, 6, 7]
因为代价问题,bisect模块的函数不会检查列表是否已经排序,对未排序列表使用bisect的函数虽然不会报错,但可能会导致不正确的结果。
切片
使用切片可以选取子集,也可以用来赋值切片包含起始位置start,不包含结尾stop,元素数量:stop-start切片索引省略会默认传入起始位置或结束位置负索引可以从序列的尾部进行索引步进值step可以在第二个冒号后面使用,意思是每隔多少个数取一个值;对列表或元组进行翻转时,向步进传值-1 内建序列函数
enumerate
遍历序列时,同时追踪当前元素的索引,使用enumerate函数
for i,value in enumerate(collection):
# 使用值做点事
对数据建立索引,使用enumerate构造字典
some_list = ['foo','bar','baz']
mapping = {}
for i,v in enumerate(some_list):
mapping[v] = i
mapping # 输出 {'foo': 0, 'bar': 1, 'baz': 2}
sorted
sorted函数返回一个根据任意序列中的元素新建的已排序列表
zip
zip将列表、元组或其他序列的元素配对,新建一个元组构成的列表
zip将列表、元组或其他序列的元素配对,新建一个元组构成的列表
seq1 = ['foo','bar','baz']
seq2 = ['one','two','three']
zipped = zip(seq1,seq2)
list(zipped)
# 输出:[('foo', 'one'), ('bar', 'two'), ('baz', 'three')]
seq3 = [False,True]
list(zip(seq1,seq2,seq3))
# 输出:[('foo', 'one', False), ('bar', 'two', True)]
zip常用场景:和enumerate同时使用同时遍历多个序列
for i,(a,b) in enumerate(zip(seq1,seq2)):
print(f'{i}:{a},{b}')
"""
0:foo,one
1:bar,two
2:baz,three
"""
zip“拆分”已“配对”的序列,也就是将行的列表转换为列的列表
pitchers = [('Nolan','Ryan'),('Roger','Clemens'),('Schilling','Curt')]
first_names,last_names = zip(*pitchers)
first_names # ('Nolan', 'Roger', 'Schilling')
last_names # ('Ryan', 'Clemens', 'Curt')
reversed
reversed函数可以将序列的元素倒序排列,它是一个生成器,在没有进行实例化的时候,它并不会产生一个倒序的列表 字典
字典,也是哈希表和关联数组
用大括号{}创建字典,在字典中用逗号将键值对分隔
用del关键字或pop方法删除值,pop方法会在删除的同时返回被删的值,并删除键
keys方法和values方法可以提供字典键、值的迭代器
update方法将两个字典合并
update方法改变了字典中元素位置,用del关键字或pop方法删除值,pop方法会在删除的同时返回被删的值,并删除键。
从序列生成字典
字典本质上是2-元组(含有2个元素的元组)的集合,字典是可以接受一个2-元组的列表作为参数的
mapping = dict(zip(range(5),reversed(range(5))))
mapping
# 输出:{0: 4, 1: 3, 2: 2, 3: 1, 4: 0}
默认值
字典的get方法和pop方法可以返回一个默认值
value = some_dict.get(key,default_value)
带有默认值的get方法会在key参数不是字典的键时返回None,而pop会抛出异常
常用场景:字典中的值集合通过设置成为另外一种值集合,例子:根据首字母分类
words = ['apple','bat','bar','atom','book']
by_letter = {}
for word in words:
letter = word[0]
if letter not in by_letter:
by_letter[letter] = [word]
else:
by_letter[letter].append(word)
by_letter # 输出:{'a': ['apple', 'atom'], 'b': ['bat', 'bar', 'book']}
字典的setdefault方法就是为了这个目的而生,上述循环可以改写成
for word in words: letter = word[0] by_letter.setdefault(letter,[]).append(word)
defaultdict类:向字典中传入类型或能在各位置生成默认值的函数,生成符合要求的字典
from collections import defaultdict
by_letter = defaultdict(list)
for word in words:
by_letter[word[0]].append(word)
有效的字典键类型
字典的值可以是任何Python对象,但是键必须是不可变对象,比如标量类型(整数、浮点数、字符串)或元组(且元组内对象也必须是不可变对象)哈希化:通过hash函数可以检查一个对象是否可以哈希化(即是否可以用作字典的键)为了将列表作为键,一种方式就是将其转换为元组,而元组只要它内部元素都可以哈希化,则它自己也可哈希化 集合
集合是一种无序且元素唯一的容器
创建方式
set函数 set([2,2,2,1,3,3])字面集与大括号的语法{2,2,2,1,3,3}
集合支持数学上的集合操作:联合、交集、差集、对称差集。
并集:a.union(b),a|b
交集:a.intersection(b),a&b
集合操作
大型集合使用“|=”和“&=”更高效
和字典类似,集合的元素必须是不可变的。如果想要包含列表型的元素,必须先转换为元组
检查一个集合是否是另一个结合的子集(包含于)或超集(包含)
a_set = {1,2,3,4,5}
{1,2,3}.issubset(a_set) # True
a_set.issuperset({1,2,3}) # True
{1,2,3} == {3,2,1} # True
# 当且仅当两个集合的内容一模一样时,两个集合才相等
列表、集合和字典的推导式
过滤容器元素生成新的列表:[expr for val in collection if condition],与下面for循环等价
result = []
for val in collection:
if Condition:
result.append(expr)
过滤出字符串列表长度大于2的
strings = ['a','as','bat','car','dove','python'] [x.upper() for x in strings if len(x)>2] # 输出:['BAT', 'CAR', 'DOVE', 'PYTHON']
字典推导式
dict_comp = {key-expr:value-expr for value in collection if condition}
推导式实现一个集合包含列表中字符串的长度
unique_lengths = {len(x) for x in strings}
unique_lengths
# {1, 2, 3, 4, 6}
使用map函数更函数化、更简洁地表达
set(map(len,strings))
嵌套列表推导式
可以嵌套多层列表推导式 3.2 函数
函数使用def关键字声明,return关键字返回可以有多条返回语句,如果到结尾仍然没有return语句,自动返回None每个函数都有位置参数和关键字参数
关键字参数常用于指定默认值或可选参数关键字参数必须跟在位置参数后可以按任意顺序指定关键字参数,不必强行记住参数的顺序,而只需用参数名指定 命名空间、作用域和本地函数
函数两种连接变量的方式:全局、本地
描述变量作用域的名称是命名空间
在函数内部,任意变量默认分配到本地命名空间,在函数被调用时生成,执行结束后被销毁
在函数外声明变量,可以在外部给变量赋值,但是变量必须用global关键字声明为全局变量
返回多个值通常全局变量用来存储系统中的某些状态
大量使用全局变量时,表明需要面向对象编程
返回多个值实际是返回了一个对象,也就是元组,而元组又被拆包为多个结果变量 函数是对象
Python函数是对象,很多在其他语言中比较难的构造在Python中很容易实现,例子:去除用户提交数据中的凌乱字符
import re
def clean_strings(strings):
result = []
for value in strings:
value = value.strip()
value = re.sub('[! #?]','',value)
value = value.title()
result.append(value)
return result
states = states = [' Alabama ', 'Georgia!', 'Georgia', 'georgia', 'FlOrIda','south carolina##', 'West virginia?']
clean_strings(states)
"""
['Alabama',
'Georgia',
'Georgia',
'Georgia',
'Florida',
'Southcarolina',
'Westvirginia']
"""
函数化的模式可以使你在更高层次上方便地修改字符串变换方法,例子:将特定的列表操作应用到某个特定的字符串集合上
def remove_punctuation(value):
return re.sub('[! #? ]','',value)
clean_ops = [str.strip,remove_punctuation,str.title]
def clean_strings(strings,ops):
result = []
for value in strings:
for function in ops:
value = function(value)
result.append(value)
return result
clean_strings(states,clean_ops)
"""
['Alabama',
'Georgia',
'Georgia',
'Georgia',
'Florida',
'Southcarolina',
'Westvirginia']
"""
可以将函数作为一个参数传给其他的函数,比如内建的map函数
匿名(Lambda)函数
匿名函数是一种通过单个语句生成函数的方式,其结果是返回值
匿名函数使用lambda关键字定义,该关键字仅表达“我们声明一个匿名函数”的意思
匿名函数代码量小(也更为清晰),将它作为参数进行传值,比写一个完整的函数或者将匿名函数赋值给局部变量更好。在数据分析中非常方便
假设你想要根据字符串中不同字母的数量对一个字符串集合进行排序,可以将一个匿名函数传给列表的sort方法
strings = ['foo','card','bar','aaaa','abab'] strings.sort(key = lambda x:len(set(list(x)))) strings # 输出:['aaaa', 'foo', 'abab', 'bar', 'card']
和def关键字声明的函数不同,匿名函数对象自身并没有一个显式的__name__属性
柯里化:部分参数应用
柯里化是计算机科学术语(以数学家Haskell Curry命名),它表示通过部分参数应用的方式从已有的函数中衍生出新的函数
示例
实现两个函数相加
def add_numbers(x,y):
return x+y
衍生出一个只有一个变量的新函数,add_five,可以给参数加上5
add_five = lambda y:add_numbers(5,y)
内建的functools模块可以使用Pratial函数简化处理
from functools import partial add_five = partial(add_numbers,5)生成器
迭代器协议是一种令对象可遍历的通用方式大部分以列表或列表型对象为参数的方法都可以接收任意的迭代器对象。包括内建方法比如min、max和sum,以及类型构造函数比如list和tuple生成器一次返回多个结果序列,产生一个元素后暂停,直到下一个请求。创建生成器需要将return替换为yield关键字实际调用生成器时,代码并不会立即执行,直到你请求生成器中的元素时,它才会执行它的代码。
生成器表达式
用生成器表达式来创建生成器更简单,与列表推导式类似,把其中括号换成小括号即可。生成器表达式可以作为函数参数用于替代列表推导式
itertools模块
标准库中的itertools模块是适用于大多数数据算法的生成器集合
例子:groupby可以根据任意的序列和一个函数,通过函数的返回值对序列中连续的元素进行分组
import itertools
first_letter = lambda x:x[0]
names = ['Alan', 'Adam', 'Wes', 'Will', 'Albert', 'Steven']
for letter, names in itertools.groupby(names, first_letter):
print(letter, list(names)) # names is a generator
"""
A ['Alan', 'Adam']
W ['Wes', 'Will']
A ['Albert']
S ['Steven']
"""
一些有用的内建工具库信息
错误和异常处理
def attempt_float(x):
try:
return float(x)
except (TypeError, ValueError):
return x
无论try是否报错都要执行,使用finally
f = open(path,'w')
try:
write_to_file(f)
finally:
f.close()
使用else来执行当try代码块成功执行时才会执行的代码
f = open(path,'w')
try:
write_to_file(f)
except:
print('Failed')
else:
print('Succeeded')
finally:
f.close()
IPython中的异常
报错时,IPython将会默认打印出完整的调用堆栈跟踪(报错追溯)。使用%xmode命令来控制上下文的数量,可以从Plain(普通)模(与标准Python解释器一致)切换到Verbose(复杂)模式(可以显示函数的参数值以及更多有用信息)。 文件与操作系统
打开文件进行读取或写入,需要使用内建函数open和绝对、相对路径
path = 'examples/segismundo.txt' f = open(path)
默认文件以只读方式打开,可以像处理列表一样遍历文件f中的内容
for line in f: pass
行结尾标识(EOL)完整,文件内容被全部读出,一些代码的功能就是从文件中读出不带EOL的内容列表
使用open来创建文件对象时,在结束时显式关闭文件非常重要,关闭文件会将资源释放回操作系统。
f.close()
使用with语句关闭
with open(path) as f:
lines = [x.rstrip() for x in f]
写下f=open(path, ‘w’),一个新的文件会examples/segismundo.txt的位置被创建(请小心!),并在同一路径下覆盖同名文件。
’x’文件模式,它会创建可写的文件,但如果给定路径下已经存在同名文件就会创建失败
可读文件常用方法:read、seek、tell
read返回文件中一定量的字符,通过读取的字节数来推进文件句柄的位置ell方法可以给出句柄当前的位置:f.tell()seek方法可以将句柄位置改变到文件中特定的字节:f.seek(3)
使用sys模块来检查文件的默认编码
import sys sys.getdefaultencoding() #'utf-8'
Python文件模式
写文件:使用文件对象的write或wirtelines方法
重要的Python文件方法或属性
字节与Unicode文件
根据文本编码,你可能会将字节解码为str对象,但是只有每个已编码的Unicode字符是完整的情况下,才能进行解码文本模式下,利用open方法的参数encoding可以将文件内容从Unicode编码转换为其他类型的编码除了二进制模式,在打开文件时使用seek要当心。如果文件的句柄位置恰好在一个Unicode符号的字节中间时,后续的读取会导致错误常常需要在非ASCⅡ文本数据上进行数据分析,那么精通Python的Unicode功能是很有必要的,具体参见官方文档。



