本文主要内容

  序列类型分类:

    (1)容器序列、扁平序列

    (2)可变序列、不可变序列

  列表推导式

  生成器表达式

  元组拆包

  切片

  排序(list.sort方法和sorted函数)

  bisect

python高级——目录

  文中代码均放在github上:https://github.com/ampeeg/cnblogs/tree/master/python高级

序列类型分类

  所谓序列,即元素有序排列,python标准库用C实现了丰富的序列类型,按照序列中是否可存放不同类型的数据分为"容器序列"和"扁平序列"。

  容器序列可以存放统统类型的数据,而扁平序列只能存放一种类型

    容器序列:list、tuple、collections.deque   
    扁平序列:str、bytes、bytearray、memoryview、array.array
  
  按照是否能修改的标准序列又可分为"可变序列"和"不可变序列":
    可变序列:list、bytearrary、array.arrary、collections.deque和memoryview
    不可变序列:tuple、str和bytes   由于可变序列继承自不可变序列,所以可变序列继承的方法也较多,下面看看它们包含的方法:
方法名 不可变序列 可变序列
__contains__  有 有 
__iter__  有  有 
 __len__  有  有 
__getitem__   有  有 
__reversed__   有  有 
index   有  有 
count   有  有 
__setitem__    有 
__delitem__   有 
insert   有 
append   有 
reverse   有 
extend   有 
pop   有 
remove   有 
__iadd__    有 

  

  我们以tuple和list类型为例,对比源代码中的方法,可以明显发现list的方法多于tuple:

  

列表推导式

# 列表推导式生成的是列表,会占用系统内存
# 基本语法 list_1 = [x for x in range(1, 20)]
list_2 = [x ** 2 for x in range(1, 20)] print(list_1) # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
print(list_2) # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361] # 笛卡尔积型的列表推导式
list_3 = [(x, y) for x in range(1, 3) # 1,2
for y in range(7, 10)] # 7、8、9 # 该表达式会先将1分别和7、8、9组合,然后再拿2和7、8、9组合,共6对
print(list_3) # [(1, 7), (1, 8), (1, 9), (2, 7), (2, 8), (2, 9)] list_4 = [x+y for x in range(1, 3)
for y in range(7, 10)] print(list_4) # [8, 9, 10, 9, 10, 11] # 还可以添加if语句
l = [1, 3, 4, 33, 45, 36, 422, 34, 67, 23, -4, -7, -345, 46, -6, -45, 32, -8, -4, 67, -4] list_5 = [x for x in l if x > 0] # 只取出大于0的生成列表
print(list_5) # [1, 3, 4, 33, 45, 36, 422, 34, 67, 23, 46, 32, 67]

生成器表达式

# 虽然列表推导式可以用来初始化元组、数组或其他序列类型,但是列表推导式会直接生成列表,占用内存
# 而生成器遵守了迭代器协议,可以逐个产出元素,而不是先建立一个完整的列表 # 生成器表达式直接将推导式的方括号换成圆括号即可 g = (x for x in range(1, 10000)) print(g) # <generator object <genexpr> at 0x105c0efc0> :生成器对象 from collections import Iterable, Iterator if isinstance(g, Iterable):
print("iterable") # 输出iterable: 说明生成器g是可迭代的 if isinstance(g, Iterator):
print("iterator") # 输出iterator:说明生成器g是迭代器

  下面我们来对比一下列表推导式和生成器的效率

# 比较列表推导式和生成器
import time start_time = time.time()
l = [x for x in range(1000000)]
print(time.time() - start_time) # 0.1361069679260254 start_time = time.time()
g = (x for x in range(1000000))
print(time.time() - start_time) # 1.1205673217773438e-05 # 可见,生成器远快于推导式

元组拆包

# 我们经常这样给两个变量同时赋值
a, b = 1, 2
print(a, b) # 1 2 # 还可以这样
a, b = [1, 2]
print(a, b) # 1 2 # 也可以这样
a, b = (1, 2)
print(a, b) # 1 2 # 甚至可以这样
a, b = "ab"
print(a, b) # a b '''
像以上这样连续的赋值方式,右边可以使用逗号隔开;也可以是序列。 当拆包赋值的是序列时,python解释器会先找该序列中的__iter__方法,如果该方法不存在,则寻找__getitem__方法。 接下来说其他用法
''' # 赋值后优雅地交换两个变量
a, b = (1, 2)
a, b = b, a
print(a, b) # 2 1 # 使用*号来处理多余的数据
a, b, *s = [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(a, b, s) # 1 2 [3, 4, 5, 6, 7, 8, 9]
# 这样从第三个元素开始的所有值都赋给了s a, b, *s = (1, 2, 3, 4, 5, 6, 7, 8, 9)
print(a, b, s) # 1 2 [3, 4, 5, 6, 7, 8, 9]
# 注意,本来是元组,赋之后的s变成了列表. 如果s为空的话也会返回空列表 *s, a, b = (1, 2, 3, 4, 5, 6, 7, 8, 9)
print(s, a, b) # [1, 2, 3, 4, 5, 6, 7] 8 9
# *s也可以放在前面 a, *s, b = (1, 2, 3, 4, 5, 6, 7, 8, 9)
print(a, s, b) # 1 [2, 3, 4, 5, 6, 7, 8] 9
# *s也可以放在中间 # 嵌套元组拆包
a, b, (c, d) = (1, 2, (3, 4))
print(a, b, c, d) # 1 2 3 4
# 只要按照右边的形式就可赋值 a, b, *c = (1, 2, (3, 4))
print(a, b, c) # 1 2 [(3, 4)]
 ################################
#
# 以下的例子用以说明拆包赋值时,解释器会按照__iter__、__getitem__的顺序调用类中的方法
#
################################
class Foo:
def __init__(self, s):
self.s = s def __iter__(self):
print("iter")
return iter(self.s) def __getitem__(self, item):
return self.s[item] if __name__ == "__main__":
foo = Foo("sdfafasfasf")
a, b, *s = foo
print(a, b)

拆包赋值的内部实现

  之前我们通过源码已经对比过list和tuple类中的方法和属性,下面列出《流畅的python》整理的列表和元组的方法及属性:

表 列表或元组的方法和属性

  列  表 元  组 说  明
s.__add__(s2)
· · s1 + s2 , 拼接
s.__iadd__(s2) ·   s1 += s2,就地拼接
s.append(e) ·   在尾部添加一个新元素
s.clear() ·   删除所有元素
s.__contains__(e) · · s是否包含e
s.copy() ·   列表的浅复制
s.count(e) · · e在s中出现的次数
s.__delitem__(p) ·   把位于p的元素删除
s.extend(it) ·   把可迭代对象it追加给s
s.__getitem__(p) · · s[p],获取位置p的元素
s.__getnewargs__()   · 在pickle中支持更加优化的序列化
s.index(e) · · 在s中找到元素e第一次出现的位置
x.insert(p,e) ·   在位置p之前拆入e
s.__iter__() · · 获取s的迭代器
s.__len__() · · len(s),长度
s.__mul__(n) · · s * n,n个s的重复拼接
s.__imul__(n) ·   s *= n,就地城府拼接
s.__rmul__(n) · · n * s,反向拼接*
s.pop([p]) ·   删除最后或者是位于p的元素,并返回它的值
s.remove(e) ·   删除s中第一次出现的e
s.reverse() ·   就地把s的元素倒序排列
s.__reversed__() ·   返回s的倒序迭代器
s.__setitem__(p,e) ·   s[p]=e,把元素e放在位置p,替代已经在那个位置的元素
s.sort([key], [reverse]) ·   就地对s中的元素进行排序,可选的参数有key和是否倒序reverse

  

  说明:以上元组中不加黑点的不代表一定不能这样使用,只是其作用和列表不同(说明里面有解释)。例如两个元组a和b进行增量赋值a+=b也是可以的,只是这个操作不是就地拼接,而是生成了新的元组。

切片

'''
在python中,内置的序列类型都支持切片操作,切片操作的用法十分简单:
list[start: stop: step] , 其中不包括区间范围内最后一个(事实上这是python的风格,一般不包含区间最后一个)
python里面能使用切片操作是因为实现了__getitem__方法,切片时会给该方法传递slice(start: stop: step) 参数
''' if __name__ == "__main__":
# 基本操作
l = [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(l[2:]) # 第3个元素到最后 :[3, 4, 5, 6, 7, 8, 9]
print(l[:3]) # 第一个元素到最后 :[1, 2, 3] s = "abcdefghijklmn"
print(s[2::2]) # 从第三个字母开始,隔一个字母取一个 : cegikm
print(s[::-1]) # 倒序排列 : nmlkjihgfedcba
print(s[::-2]) # 倒序隔一个取一个 nljhfdb
print(s[-2::-2]) # 倒序第二隔开始,隔一个取一个 # 利用切片赋值
l[2:5] = [20, 30]
print(l) # [1, 2, 20, 30, 6, 7, 8, 9]
try:
l[2:5] = 40 # 报错:TypeError: can only assign an iterable
# 利用切片赋值时传入的必须是可迭代对象
except Exception as e:
print(e) # can only assign an iterable
l[2:5] = (40,)
print(l) # [1, 2, 40, 7, 8, 9]
l[2:3] = "sajfljls" # 字符串属于序列,也可以迭代
print(l) # [1, 2, 's', 'a', 'j', 'f', 'l', 'j', 'l', 's', 7, 8, 9]

排序(list.sort方法和sorted函数)

'''
list.sort方法和sorted内置函数都有排序的功能,区别如下
list.sort是就地排序列表,不会把原列表复制一份。该方法返回None,以提醒不会新建一个列表。
sorted函数会新建一个列表作为返回值,这个函数可以接受任何可迭代对象,甚至包括不可变序列或生成器,最后返回的总是列表。 list.sort和sorted都有两个参数:
reverse:默认为False,设定为True以降序排列
key:一个只有一个参数的函数,这个函数会作用于序列的每一个元素上,然后以该函数的结果作为关键字排序 ''' if __name__ == "__main__":
# 1、list.sort就地排序,而sorted返回列表
l = [x for x in range(10, 0, -1)] # 初始化一个列表:[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
print(id(l), l) # l最初的地址:4536449800 [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
l.sort()
print(id(l), l) # 排序后的地址:4536449800 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# l前后的的地址没变,说明是就地排序 l = [x for x in range(10, 0, -1)] # 初始化一个列表:[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
print(id(l), l) # l最初的地址:4415318984 [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
l = sorted(l)
print(id(l), l) # 排序后的地址:4415318792 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # 2、sorted可以接受任何可迭代对象
l = (x for x in range(10, 0, -1))
print(type(l)) # 迭代器 <class 'generator'>
print(sorted(l)) # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] s = "qwertyuiopasdfghjklzxcvbnm" # 字符串序列
print(sorted(s)) # ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'] s = (1, 3, 2, 456, 345, 12, 2, 5, 78, 34) # 不可变元组
print(sorted(s)) # [1, 2, 2, 3, 5, 12, 34, 78, 345, 456] # 3、reverse参数
s = "qwertyuiopasdfghjklzxcvbnm"
print(sorted(s, reverse=True)) # ['z', 'y', 'x', 'w', 'v', 'u', 't', 's', 'r', 'q', 'p', 'o', 'n', 'm', 'l', 'k', 'j', 'i', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a'] # 4、key参数
s = "QwERTYuioPaSdfGHjKLzXcvbnm"
print(sorted(s)) # ['E', 'G', 'H', 'K', 'L', 'P', 'Q', 'R', 'S', 'T', 'X', 'Y', 'a', 'b', 'c', 'd', 'f', 'i', 'j', 'm', 'n', 'o', 'u', 'v', 'w', 'z']
print(sorted(s, key=str.lower)) # 忽略大小写 ['a', 'b', 'c', 'd', 'E', 'f', 'G', 'H', 'i', 'j', 'K', 'L', 'm', 'n', 'o', 'P', 'Q', 'R', 'S', 'T', 'u', 'v', 'w', 'X', 'Y', 'z']
print(sorted(s, key=str.upper)) # 也是忽略大小写
##########################
#
# 以下自定义一个类也可使用sorted函数
#
########################## class Obj:
def __init__(self):
self.s = [x for x in range(10, 0, -1)] def __getitem__(self, item):
print("getitem")
return self.s[item] def __repr__(self):
return str(self.s) def __iter__(self):
return iter(self.s) obj = Obj()
print(obj) # [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] # 添加getitem后可以使用sorted函数 (实验时请注视掉getitem方法)
print(sorted(obj)) # 打印10次getitem , [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # 添加iter方法
print(sorted(obj)) # 此时解释器会先调用iter方法,不会再使用getitem方法
# [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

使自定义类也可使用sorted函数调用

bisect

'''
bisect模块主要用来管理有顺序的序列
bisect模块包含的主要函数是bisect和insort,两个函数都使用二叉树方法搜索
1、bisect(haystack, needle)
haystack必须是一个有序的序列,该函数搜索needle在haystack中的位置,该位置使得将needle插入后haystack仍然升序
查找到位置后可用haystack.insert()插入 2、insort(seq, item)
把item插入到seq中,并能保持seq的升序 ''' # 本人认为《流畅的python》中的对该模块介绍的例子比较经典,故引用之 # 1、关于bisect.bisect的示例
import bisect
import sys HAYSTACK = [1, 4, 5, 6, 8, 12, 15, 20, 21, 23, 23, 26, 29, 30]
NEEDLES = [0, 1, 2, 5, 8, 10, 22, 23, 29, 30, 31] ROW_FMT = '{0:2d} @ {1:2d} {2}{0:<2d}' def demo(bisect_fn):
for needle in reversed(NEEDLES):
position = bisect_fn(HAYSTACK, needle)
offset = position * ' |'
print(ROW_FMT.format(needle, position, offset)) if __name__ == '__main__': if sys.argv[-1] == 'left':
bisect_fn = bisect.bisect_left
else:
bisect_fn = bisect.bisect print('DEMO:', bisect_fn.__name__)
print('haystack ->', ' '.join('%2d' % n for n in HAYSTACK))
demo(bisect_fn) ''' 输出如下
DEMO: bisect
haystack -> 1 4 5 6 8 12 15 20 21 23 23 26 29 30
31 @ 14 | | | | | | | | | | | | | |31
30 @ 14 | | | | | | | | | | | | | |30
29 @ 13 | | | | | | | | | | | | |29
23 @ 11 | | | | | | | | | | |23
22 @ 9 | | | | | | | | |22
10 @ 5 | | | | |10
8 @ 5 | | | | |8
5 @ 3 | | |5
2 @ 1 |2
1 @ 1 |1
0 @ 0 0
'''
# 另,bisect.bisect函数有两个可选参数——lo和hi来缩小搜索范围,lo的默认值是0,hi的默认值是序列的长度
# 再另,bisect.bisect函数其实是bisect_right函数的别名,还有一个bisect_left,插入位置如果有相等的元素时,插入元素会放在它相等的
# 元素后面,后者会放在前面 # 根据分数,查到等级 def grade(score, breakpoints=[60, 70, 80, 90], grades = 'FDCBA'):
i = bisect.bisect(breakpoints, score) # 这里的bisect.bisect实际上使用的是bisect_right
return grades[i] print([grade(score) for score in [33, 55, 90, 87, 65, 78, 34, 60, 100]])
# 2、关于bisect.insort函数

import bisect
import random SIZE = 7 random.seed(1729) my_list = []
for i in range(SIZE):
new_item = random.randrange(SIZE*2)
bisect.insort(my_list, new_item)
print('%2d ->' % new_item, my_list) '''输出:
10 -> [10]
0 -> [0, 10]
6 -> [0, 6, 10]
8 -> [0, 6, 8, 10]
7 -> [0, 6, 7, 8, 10]
2 -> [0, 2, 6, 7, 8, 10]
10 -> [0, 2, 6, 7, 8, 10, 10]
''' # 另,insort函数也有insort_left,背后使用的是bisect_left

python高级系列文章目录

python高级——目录

python高级(二)—— python内置序列类型的更多相关文章

  1. Python 数据类型常用的内置方法(二)

    目录 Python 数据类型常用的内置方法(二) 1.字符串类型常用内置方法 1.upper.lower.isupper.islower 2.startswith.endswith 3.format ...

  2. python匿名函数 与 内置函数

      一.匿名函数  1.定义: 匿名函数顾名思义就是指:是指一类无需定义标识符(函数名)的函数或子程序. 2.语法格式:lambda 参数:表达式 lambda语句中,开头先写关键字lambda,冒号 ...

  3. Python 的基本运算和内置函数

    一.运算符 (一)Python算术运算符 以下假设变量: a=10,b=20: 运算符 描述 实例 + 加 - 两个对象相加 a + b 输出结果 30 - 减 - 得到负数或是一个数减去另一个数 a ...

  4. python 类(object)的内置函数

    python 类(object)的内置函数 # python 类(object)的内置函数 ### 首先 #### 以__双下划线开头的内置函数 __ #### __往往会在某些时候被自动调用,例如之 ...

  5. Python 数据类型常用的内置方法(三)

    目录 Python 数据类型常用的内置方法(三) 1.列表内置方法 1.sort():升序 2.reverse():颠倒顺序 3.列表比较运算 2.字典内置方法 1.对Key的操作 2.len( )- ...

  6. python — lambda表达式与内置函数

    目录 1 lambda表达式 (匿名函数) 2 内置函数 1 lambda表达式 (匿名函数) 用于表示简单的函数 lambda表达式,为了解决简单函数的情况: def func(a1,a2): == ...

  7. python 匿名函数,内置函数

    一 :匿名函数 匿名就是没有名字 def func(x,y,z=1): return x+y+z 匿名 lambda x,y,z=1:x+y+z #与函数有相同的作用域,但是匿名意味着引用计数为0,使 ...

  8. Python之生成器及内置函数篇4

    一.可迭代对象 #1.什么是迭代?:迭代是一个重复的过程,并且每次重复都是基于上一次的结果而来 #2.可迭代的对象:在python中,但凡内置有__iter__方法的对象,都是可迭代的对象 #3.迭代 ...

  9. 《Python》反射、内置方法(__str__,__repr__)

    一.反射 通过字符串的形式操作对象相关的属性.(使用字符串数据类型的变量名来获取这个变量的值) Python中的一切事物都是对象(都可以使用反射) 反射类中的变量 反射对象中的变量 反射模板中的变量 ...

随机推荐

  1. C++细节理解

    1.为什么static类外初始化不需要static关键字 答:因为类外static变量或函数表示限定在此源文件中才能使用,而类中的static变量或函数表示由本类及其所有对象共享,如果在类外初始化或定 ...

  2. 向对象(OO)程序设计

    http://www.uml.org.cn/mxdx/201208232.asp 前言 本文主要介绍面向对象(OO)程序设计,以维基百科的解释: 面向对象程序设计(英语:Object-oriented ...

  3. Mybatis:传入参数方式以及#{}与${}的区别

    一.在MyBatis的select.insert.update.delete这些元素中都提到了parameterType这个属性.MyBatis现在可以使用的parameterType有基本数据类型和 ...

  4. css实现二级菜单显示和收缩

    <ul class="nav"> <li><a href="#">一级菜单</a> <ul class=& ...

  5. springMvc 核心配置

    <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.spr ...

  6. 用Word 写csdn blog

    目前大部分的博客作者在用Word写博客这件事情上都会遇到以下3个痛点: 1.所有博客平台关闭了文档发布接口,用户无法使用Word,Windows Live Writer等工具来发布博客.使用Word写 ...

  7. java.awt.Graphics2D 图片缩放

    关键字:java image  thumbnail  google 粗略demo: import java.awt.Graphics2D; import java.awt.GraphicsConfig ...

  8. 微信openid

    微信openid由用户id和公众号id加密而来,同一用户相对同一公众账号的openid是不变的.

  9. Adding Cache-Control headers to Static Files in ASP.NET Core

    Thanks to the ASP.NET Core middleware pipeline, it is relatively simple to add additional HTTP heade ...

  10. Linux Qt 5.x 环境搭建

    Step 1 从Qt官网下载 qt-opensource-linux-x64...run 在linux命令行中给予文件可执行权限 $ chmod u+x qt-opensource-linux...r ...