导语:本文章记录了本人在学习Python基础之数据结构篇的重点知识及我的心得,以加深本身的理解。
本文重点:html
一、了解列表、元组、字节序列、数组等数据结构;
二、了解上述数据结构相对应的迭代、切片、排序、拼接操做;
三、若是想把代码写的Pythonic,在保证代码可读性的前提下,代码行数越少越好。
容器序列可以存放不一样类型的数据,比扁平序列更灵活;
扁平序列只能存放一种类型的原子性的数据,体积更小速度更快。eg:数字,字符字节python
一、列表推导:在[]中使用命令语句加for甚至if实现迭代推导出新列表的操做。
列表推导运用得当将使得代码清晰优雅。
Python3中不存在Python2的列表推导变量泄漏问题,即列表推导中的局部变量与主程序的同名变量引用冲突问题。算法
eg1:利用ord()把单词apple的ASCII码以列表存起来。express
list_a=[ord(x)for x in "apple"]#ord()是返回字符对应ASCII码的函数。 print(list_a)#输出:[97, 112, 112, 108, 101]
eg2:使用列表嵌套循环求笛卡尔积。数组
Clothes=["T-shirt","sweater"] Color=["red","yellow","blue"] Product=[(x,y)for x in Clothes for y in Color]#嵌套循环 print(Product)#输出:[('T-shirt', 'red'), ('T-shirt', 'yellow'), ('T-shirt', 'blue'), ('sweater', 'red'), ('sweater', 'yellow'), ('sweater', 'blue')]
句法提示
:Python会忽略[ ],{ },( )中的换行符" ",把括号内的多行内容视为一行。所以可在列表、列表推导、字典、生成器表达式中省略换行符。安全
二、学习生成器表达式应先了解的概念:
迭代器协议:对象必须提供next方法,执行该方法会返回迭代中的下一项,直到引发stopiteration中止迭代。
可迭代对象:实现迭代器协议的对象(实现方法:对象内部定义__iter__方法)任何可迭代对象均可以使用for循环。由此看出Python的for循环抽象程度高于Java。
数据结构
使用Iterable判断一个对象是否可迭代:
app
from collections import Iterable print(isinstance("apple", Iterable))#True可迭代 print(isinstance(100, Iterable))#False不可迭代
三、生成器表达式:按需返回一个结果对象,而非先构建一个完整的列表。
表达方法:语句格式与列表推导相似,但须在圆括号()中书写。
特色:按需产值,延迟计算,节省内存,在生成大规模序列类型具有优点。
dom
eg1:用生成器表达式初始化元组函数
tuple_a=(ord(x)for x in "apple") for i in tuple_a: print(i)
eg2:用生成器表达式初始化数列
from array import array ascii=array('I',(ord(x)for x in "apple")) for i in ascii: print(i)
eg3:用生成器表达式计算笛卡尔积
Clothes=["T-shirt","sweater"] Color=["red","yellow","blue"] tuple_Product=((x,y)for x in Clothes for y in Color) for i in tuple_Product:#生成器表达式是按需产值,没法用print直接一次性输出,须要配合for迭代输出 print(i)
元组有两重功能,一是看成记录来用的数据模型,二是充当不可变的列表,
元组用做记录时,能够理解为数据+位置。之因此提位置是由于有序的位置赋予了数据独特的意义。若是此时对元组进行排序等打乱位置的操做,会使得元组丢失本来所携带的信息。
Traveler_ids=[("hoya","85236669"),("Dennis","O7856P77"),("Sky","L336636")] for i in Traveler_ids: print("%s/%s"%i)#平行赋值,元组拆包 #输出: hoya/85236669 Dennis/O7856P77 Sky/L336636
for name,_ in Traveler_ids:#对于拆包中不感兴趣的一类元素能够用占位符“_" print(name) #输出: hoya Dennis Sky
元组拆包能够应用到任何可迭代对象上,惟一的硬性要求是,被可迭代对象中的元素数量必须与接受这些元素的元组的空挡数一致。
t=(9,4) print(divmod(*t))#利用*能够把一个可迭代对象拆开做为函数的参数,输出为(2, 1) a=3 b=5 a,b=b,a#不使用中间变量实现值的交换 print(a,b)
用*来处理剩下的元素:星号*能够把不肯定参数装到一块儿。注意在平行赋值中,星号*只能用在一个变量名前面,可是这个变量能够出如今赋值表达式的任意位置。
a,b,*rest=range(5) print(a,b,rest)#输出为:0 1 [2, 3, 4] a,*rest,b=range(5) print(a,rest,b)#输出为0 [1, 2, 3] 4
format函数输出通常格式:.
通常格式[fill,align,sign,0,width,.precision,type],每一处都是可选的.
format函数字符串映射方式:
print("{1}|{2}|{1}".format("a","b","c"))#经过位置填充。输出:b|c|b print("{}|{}|{}".format("a","b","c"))#也能够不填写位置索引,默认按照顺序填充。输出:a|b|c
print("{Greeting},My name is {name}".format(Greeting="Hello",name="Hoya")) #输出:Hello,My name is Hoya Fruit_number={"apple":15,"pear":10,"strawberry":20} print("We have {x[apple]} apples,{x[pear]} pears,and {x[strawberry]} strawberries.".format(x=Fruit_number))#字典的key也能填充字符串。注意x是局部变量! #输出:We have 15 apples,10 pears,and 20 strawberries.
Friends=["Daniel","Dennis"] print("I have two best friends,one is {x[0]},the other is {x[1]}".format(x=Friends)) #输出:I have two best friends,one is Daniel,the other is Dennis
本质上是位置索引,由于传入的对象非具体元素而是列表,故用下标填充。
class Students: def __init__(self,x,y): self.x=x self.y=y def __str__(self): return "Hello,My name is {self.x},I come from {self.y}".format(self=self) print(Students("Hoya","China"))#传入类,把类的属性值填充到字符串中format&嵌套元组实例综合运用: cities=[("New York","USA",(40.8086,-74.0204)), ("Mexico City","Mexico",(19.4333,-99.1333)), ("Tokyo","Japan",(35.6899,169.3254)), ("Sao Paul","USA",(-23.5478,-46.6358))] print("{:15}|{:^9}|{:^9}".format("","lat","long")) x="{:15}|{:^9.2f}|{:^9.2f}"#对经纬度小数点后保留两位数字 for city,_,(lat,long) in cities :#平行赋值,对应结构一致。 if long < 0: print(x.format(city,lat,long))#输出以下: | lat | long New York | 40.80 |-74.02 Mexico City | 19.43 |-99.13 Sao Paul |-23.54 |-46.63
namedtuple:具名元组,来自标准库中的collections模块中的方法。它能够构建一个带字段名的元组和一个有名字的类。
特色:可以直接使用名字访问元素。
注意:
与tuple相同,namedtuple属性不可变!
namedtuple属性与方法:
应用实例:
from collections import namedtuple Star=namedtuple("Star", "name country coordinates age") hoya=Star("Hoya","China",(20,30),18) print(hoya.name)#读取信息的两种方式 print(hoya[0]) print(Star._fields) Jack_data=("Jack","Russia",(100,36),14) # Jack=Star(*Jack_data)#此方法与_make(iterable)类方法效果相同 Jack=Star._make(Jack_data) print(Jack.age) print(Jack._asdict()) for key,value in Jack._asdict().items():#字典内置方法items()而非item() print(key+":",value)#加号先后字符类型一致
元组除了不支持增减,逆序,就地修改等直接或间接改变元素信息及位置的方法以外,支持列表其余的全部方法。
因为方法过多不便展现,详情参考Fluent Python P27以及Python基础教程笔记列表方法。
切片通常格式s[a:b:c],c取值为负意味着反向取值。
注意:若是赋值对象是切片,赋值号另外一端的对象也必须是可迭代对象。即便单独一个值,也要把它转换成可迭代的序列。
反例:
list1=[1,3,6,9,10] list1[1:3]=2#正确形式:list1[1:3]=[2] Traceback (most recent call last): File "F:\xuexi\Fluent Python\section 2-4.py", line 3, in <module> list1[1:3]=2 TypeError: can only assign an iterable#切片右端必须是可迭代对象。
列表尽管具备灵活简单的特色,但并不能适用于各类需求,为此咱们要寻找更好的选择。下面介绍三种在某些状况下能够替换列表的数据类型。
若是咱们须要一个只包含数字的列表,那么array.array比list更高效。
构造数组的通常格式:array(typecode,[ initializer])
typecode指数组类型,经常使用的以下:initializer相似列表推导
Type code | C Type | Python Type | Minimum size in bytes |
---|---|---|---|
'b' | signed char | int | 1 |
'B' | unsigned char | int | 1 |
'i' | signed int | int | 2 |
'I' | unsigned int | int | 2 |
'l' | signed long | int | 4 |
'L' | unsigned long | int | 4 |
'f' | float | float | 4 |
'd' | double | float | 8 |
数组方法:数组支持全部跟可变序列有关的操做,
可参考Fluent Python P42以及array的Python官方library.数组从Python3.4开始不支持诸如list.sort()这种就地排序的方法。
另外数组提供读写文件更快的方法,如.frombytes和.tofile
eg:综合运用
from random import random from array import array array1=array("d",(random()for x in range(10^5)))#构建数组 fp=open("array1.bin","wb")#以二进制形式存储文件 array1.tofile(fp)#打开文档导入数据 fp.close()#关闭文档 print(array1[-1]) array2=array("d") fp=open("array1.bin","rb") array2.fromfile(fp, 10^5) fp.close() print(array2[-1]) print(array1==array2)#输出True,说明存取无误。
collections.deque类(双向队列)是一个线程安全、能够快速从两端添加或者删除的数据类型。
deque构造:deque([initializer],maxlen=some_number)#传入初值和maxlen参数。初值可在中括号中,也可采起相似列表推导的方法;maxlen限制deque的元素数量,选填。
deque方法:
注意是逆序输入(由于是逐个迭代插入的关系)
eg:
from collections import deque deque1=deque([x*x for x in range(3)],maxlen=5) print(deque1)#deque([0, 1, 4], maxlen=5) deque1.extendleft([9,16,25])#队列满元素后,一端添加的元素会使得另外一端删除同等数量的元素保持maxlen不变 print(deque1)#deque([25, 16, 9, 0, 1], maxlen=5) deque1.rotate(-3) print(deque1)#deque([0, 1, 25, 16, 9], maxlen=5) deque1.popleft() print(deque1)#deque([1, 25, 16, 9], maxlen=5)
deque实现了大部分列表所拥有的方法,具备灵活多用和线程安全
的特色;但deque从中间删除元素的操做会慢一些。注:deque不支持切片操做(我的实践经验)
memoryview实际上是泛化和去数学化的NumPy数组。
它让你在不须要复制内容的前提下在数据结构之间共享内存;数据结构能够是任何形式。
list1=[["_"]*3 for x in range(3)]
list2=[["_"]3]3
list11=1
list21=1
print(list1)#[['_', '_', '_'], ['_', 1, '_'], ['_', '_', '_']]
print(list2)#[['_', 1, '_'], ['_', 1, '_'], ['_', 1, '_']]
上文代码是两种初始化嵌套列表的方式,仔细观察发现list2的赋值后在3个子列表中均有赋值,这是错误的初始化方法。缘由在于list2初始化的子列表引用一致,这种列表每每不是咱们想要的结果。
教训:a*n语句中,若是序列a的里的元素是对其余可变对象的引用,就须要额外小心。缘由是会产生指向同一个可变对象的屡次引用!
原子操做
:不会被线程调度机制打断的操做,一旦执行将运行到结束。
eg:
cities=["New York","Mexico City","Tokyo","Sao Paul"] print(sorted(cities,key=len,reverse=True))#['Mexico City', 'New York', 'Sao Paul', 'Tokyo'] cities.sort(reverse=True) print(cities)#['Tokyo', 'Sao Paul', 'New York', 'Mexico City']
sorted通常格式:
sorted(iterable,[,key[,reverse=True]]])
eg:
students = [('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 13)] print(sorted(students, key=lambda s: s[0]))#lambda是匿名函数;s(0)表明把可迭代对象中待排序元素中的第一个元素取出比较排序,这里是诸如"john"的英文字符串。 #输出:[('dave', 'B', 13), ('jane', 'B', 12), ('john', 'A', 15)]
bisect.bisect(haystack,needle):返回一个在haystack中插入needle保持序列升序的位置。
bisect.insort(seq,item):返回一个在seq中插入item保持序列升序的新序列。
bisect和insort同样,能够额外填写lo和hi两个参数控制
To learn more:https://docs.python.org/3/lib...
eg:
import bisect list3=[1,3,5,7,9] s=8 print(bisect.bisect(list3,s))#输出:4 bisect.insort(list3,s) print(list3)#输出:[1, 3, 5, 7, 8, 9]