自定义序列
大约 9 分钟
Python的序列分为四大类。
容器序列
:list
、tuple
、deque
。扁平序列
:str
、bytes
、bytearray
、array
。可变序列
:list
、deque
、bytearray
、array
。不可变序列
:str
、tuple
、bytes
。
但常用的序列只有list(列表)
、tuple(元组)
和str
。
常用序列
class User:
pass
list1 = [1, 2, 3, 4, 5]
# append不会对参数进行迭代,而是整个作为一个值
list1.append(6)
list1.append(7)
list1 = list1 + [8]
list1 += [9, 10]
# 使用 +,不能和元组相加,下面的操作会报错 TypeError: can only concatenate list (not "int") to list
# list1 = list1 + (8)
# 使用 +=,变为元组依然可以加,+=后边的参数可以为任意的序列类型
list1 += (11, 12)
list1 += "13,14"
list1.extend("15,16")
list1.extend([17])
list1.extend(range(3))
# 因为extend没有返回值,这样写将导致list1为None
# list1 = list1.extend(range(3))
# extend不能传递元组参数
# list1.extend((18))
print(list1)
print(type(list1))
# 如果有相同的元素,则只删除第一个出现的
list1.remove(1)
print(list1)
list2 = list(range(1, 6))
print(list2)
# 列表可以存储多种不同的数据类型
list3 = [1, "2", 3.1415926, True, ["1", 2], ("1", 2), {1, "2"}, {"a":1, "b":2}, User]
print(type(list3))
print(list3)
# 列表的下标从0开始
print(list3[0])
print(list3[4][0])
print(list3[-1])
# 字符串也属于序列
str1 = "hello python"
# 判断元素是否在序列中
print("2" in str1)
# 获取序列长度
print(len(str1))
# 求最值时所有元素必须是同一种类型
print(max(str1))
print(min(str1))
print(max([1, 2, 3]))
print(min(["1", "2", "3"]))
# 维持已排序的升序序列,可有效提升系统性能
import bisect
a = []
bisect.insort(a, 3)
bisect.insort(a, 1)
bisect.insort(a, 11)
bisect.insort(a, 5)
bisect.insort(a, 21)
bisect.insort(a, 9)
print(a)
print(bisect.bisect(a, 9))
print(bisect.bisect_left(a, 9))
tuple(元组)
name_tuple = ("lixingyun", "wangli")
print(name_tuple) # ('lixingyun', 'wangli')
# 直接这样调用会报错,因为元组不可变
# name_tuple[0] = "xiaoyan"
# 但可以这样改变
name_tuple = ("shihao", "xiaoyan")
print(name_tuple) # ('shihao', 'xiaoyan')
# 元组可以包含任意类型的元素
tuple1 = (1, "2", 3.14, True, (1, 2))
# tuple没有append和extend操作
tuple1 += (9, 10)
# 不支持 += 字符串
# tuple1 += "11,12"
# 元组的访问方式同列表一样
print(tuple1[0]) # 1
# 当只有一个元素时,元组的括号变成了运算符优先级符号
print(type((1))) # <class 'int'>
# 这是tuple
print(type((1,))) # <class 'tuple'>
# 这也是tuple
print(type(())) # <class 'tuple'>
# 元组解包
unpack_tuple = 1, "2", 3.14, True
print(type(unpack_tuple)) # <class 'tuple'>
print(unpack_tuple) # (1, '2', 3.14, True)
a, b, c, d = unpack_tuple
print(a, b, c, d) # 1, "2", 3.14, True
# 解包时,元组中元素个数必须与解包变量个数相同,否则会报错
# a, b, c = unpack_tuple
# 但可以这样赋值
a, *other = unpack_tuple
print(a, other) # 1 ['2', 3.14, True]
print(a, b, c, d) # 1, "2", 3.14, True
# 列表解包
unpack_list = [1, "2", 3.14, True]
print(type(unpack_list)) # <class 'list'>
a, b, c, d = unpack_list
# 和元组解包一样,列表中元素个数必须与解包变量个数相同,否则会报错
# a, b, c = unpack_list
print(a, b, c, d) # 1, "2", 3.14, True
# 元组的不可变并不是绝对的
tuple2 = (1, ["2", 3.14, True])
print(tuple2) # (1, ['2', 3.14, True])
tuple2[1][0] = "lixingyun"
tuple2[1].append("wanglin")
print(tuple2) # (1, ['lixingyun', 3.14, True, "wanglin"])
# 元组可以作为字典的键
user_info_dict = {}
user_info_dict[(1, 2)] = "lixingyun"
print(user_info_dict) # {(1, 2): 'lixingyun'}
# 但列表就不能这样做,否则报错
# user_info_dict[[1, 2]] = "lixingyun"
namedtuple(命名元组)
# 参照命名参数,元组也实现了命名元组
from collections import namedtuple
# 通过 namedtuple 创建一个类
# namedtuple 的优势在于拥有类的属性,但却没有初始化类时的那些开销
User = namedtuple("User", ["name", "age", "gender"])
# 这等同于创建 class User
user1 = User(name="lixingyun", age=18, gender="male")
print(user1) # User(name='lixingyun', age=18, gender='male')
print(user1.age, user1.name, user1.gender) # 18 lixingyun male
# 用 tuple 初始化一个 namedtuple
user_init = ("wanglin", 19)
user2 = User(*user_init, "male")
print(user2) # User(name='wanglin', age=19, gender='male')
# 通过命名参数的方式传递
user_info_dict = {"name": "lindong", "age": 23}
user2 = User(**user_info_dict, gender="male")
print(user2) # User(name='lindong', age=23, gender='male')
# namedtuple 的属性可以通过 _asdict() 方法转换为字典
print(user2._asdict()) # {'name': 'lindong', 'age': 23, 'gender': 'male'}
# 修改元素的值,但并不会改变自身,而是返回一个新的 namedtuple
user3 = user2._replace(name="liushen", age=10000, gender="female")
print(user2) # User(name='lindong', age=23, gender='male')
print(user3) # User(name='liushen', age=10000, gender='female')
# 另一种修改 namedtuple 的方式是通过 _make() 方法,只要是可迭代对象,都可以传进来
user4 = User._make(["xiaoyan", 22, "male"])
print(user4) # User(name='xiaoyan', age=22, gender='male')
user_init = ("luofeng", 21, "male")
user5 = User._make(user_init)
print(user5) # User(name='luofeng', age=21, gender='male')
# 无法传递 dict 字典,否则打印出的结果会有问题
user6 = User._make({"name": "lixingyun", "age": 18, "gender": "male"})
print(user6) # User(name='name', age='age', gender='gender') 这个结果明显不是想要的
# 因为 namedtuple 是继承自 tuple,所以它的解包功能依旧可用
name, age, gender = user5
print(name, age, gender) # luofeng 21 male
序列切片
# 序列都支持切片操作:[start:end:step]
# 第一个数字start表示切片开始位置,默认为0
# 第二个数字end表示切片截止(但不包含)位置(默认为列表长度)
# 第三个数字step表示切片的步长(默认为1)
# 当start为0时可以省略,当end为序列长度时可以省略,当step为1时可以省略,并且省略步长时可以同时省略最后一个冒号
# 当step为负整数时,表示反向切片
slice = [3, 4, 5, 6, 7, 9, 11, 13, 15, 17]
print("1 ==>", slice[::]) # 全部采用默认值,因此返回包含原序列中所有元素的新序列
print("2 ==>", slice[::-1]) # 返回包含原序列中所有元素的逆序序列
print("3 ==>", slice[::2]) # 获取偶数位置的元素
print("4 ==>", slice[1::2]) # 获取奇数位置的元素
print("5 ==>", slice[3:6]) # 指定切片的开始和结束位置,步长=1
print("6 ==>", slice[0:100]) # 当切片结束位置大于序列长度时,从序列尾部截断
print("7 ==>", slice[100:]) # 当切片开始位置大于序列长度时,返回空序列
slice[len(slice):] = [9] # 在序列尾部增加元素
print("8 ==>", slice)
slice[:0] = [1, 2] # 在序列头部插入元素
print("9 ==>", slice)
slice[3:3] = [4] # 在序列中间位置插入元素
print("10 ==>", slice)
slice[:3] = [1, 2] # 替换序列元素,等号两边的序列长度相等
print("11 ==>", slice)
slice[3:] = [4, 5, 6] # 等号两边的序列长度也可以不相等
print("12 ==>", slice)
slice[::2] = [0] * 3 # 隔一个修改一个
print("13 ==>", slice)
slice[::2] = ['a', 'b', 'c'] # 隔一个修改一个
print("14 ==>", slice)
#slice[::2] = [1,2] # 报错,因为左侧切片不连续,等号两边序列长度必须相等
slice[:3] = [] # 删除序列中前3个元素
print("15 ==>", slice)
del slice[:3] # 连续删除切片元素
print("16 ==>", slice)
del slice[::2] # 不连续删除切片元素,隔一个删一个
print("17 ==>", slice)
可切片对象
'''
自定义一个切片对象,参照不可变序列Sequence实现:
class Sequence(Collection[_T_co], Reversible[_T_co], Generic[_T_co]):
@overload
@abstractmethod
def __getitem__(self, index: int) -> _T_co: ...
@overload
@abstractmethod
def __getitem__(self, index: slice) -> Sequence[_T_co]: ...
# Mixin methods
def index(self, value: Any, start: int = 0, stop: int = ...) -> int: ...
def count(self, value: Any) -> int: ...
def __contains__(self, value: object) -> bool: ...
def __iter__(self) -> Iterator[_T_co]: ...
def __reversed__(self) -> Iterator[_T_co]: ...
'''
import numbers
class MySlice:
def __init__(self, elements):
self.elements = elements
# 支持切片操作的关键魔术方法
def __getitem__(self, item):
return self.elements[item]
# if isinstance(item, numbers.Integral):
# return [self.elements[item]]
# elif isinstance(item, slice):
# return self.elements[item]
# else:
# raise TypeError('slice indices must be integers or slices, not {}'.format(type(item)))
def __reversed__(self):
self.elements.reverse()
def __len__(self):
return len(self.elements)
def __iter__(self):
return iter(self.elements)
def __contains__(self, item):
if item in self.elements:
return True
else:
return False
elements = ['lixingyun', 'wanglin', 'xiaoyan']
myslice = MySlice(elements)
print(len(myslice))
if 'lixingyun' in myslice:
print('lixingyun')
print(myslice[0])
print(myslice[0:10])
reversed(myslice)
for i in myslice:
print(i)
数组与队列
# 数组array只能存放指定的数据类型
import array
# typecode参数的有效取值:https://docs.python.org/3.9/library/array.html
my_array = array.array('i', [1, 2, 3, 4, 5])
my_array.append(6)
# 传入字符串会报错
# my_array.append("7")
# 传入浮点数也会报错
# my_array.append(8.18)
my_array.extend([7, 8, 9])
print(my_array)
# 删除数组中的元素
my_array.remove(1)
print(my_array)
# array的很多操作和list一样
# 不支持 += 和 +
# my_array += [10, 11]
# my_array = my_array + [10, 11]
# array也支持切片操作
print(my_array[3:])
# 双端队列deque
# deque是线程安全的,而list不是
from collections import deque
# 也可以用 array、list、tuple 或 dict 这些可迭代对象来初始化它,但用 dict 初始化时,deque只会使用 key 作为队列值
user_dict = {
'a': '1',
'b': 18
}
# 创建时限制最大长度
deque1 = deque(user_dict, maxlen=5)
deque1.append(('c', 1))
print(deque1) # deque(['a', 'b', ('c', 1)], maxlen=5)
# 从左侧添加元素,如果超过长度限制,会删除尾部最后一个元素
deque1.appendleft('c')
print(deque1) # deque(['c', 'a', 'b', ('c', 1)], maxlen=5)
deque1.appendleft('d')
print(deque1) # deque(['d', 'c', 'a', 'b', ('c', 1)], maxlen=5)
deque1.appendleft('e')
print(deque1) # deque(['e', 'd', 'c', 'a', 'b'], maxlen=5)
deque1.appendleft('f')
print(deque1) # deque(['f', 'e', 'd', 'c', 'a'], maxlen=5)
deque1.appendleft('g')
print(deque1) # deque(['g', 'f', 'e', 'd', 'c'], maxlen=5)
deque1.append('h')
print(deque1) # deque(['f', 'e', 'd', 'c', 'h'], maxlen=5)
deque1.append('i')
print(deque1) # deque(['e', 'd', 'c', 'h', 'i'], maxlen=5)
# 从右侧,也就是尾部移除元素
right = deque1.pop()
print(right) # i
print(deque1) # deque(['e', 'd', 'c', 'h'], maxlen=5)
# 从左侧,也就是头部移除元素
left = deque1.popleft()
print(left) # e
print(deque1) # deque(['d', 'c', 'h'], maxlen=5)
# 修改元素内容
deque1[2] = 'c'
deque1[0] = 'a'
deque1[1] = 'b'
print(deque1) # deque(['a', 'b', 'c'], maxlen=5)
deque2 = deque(range(5), maxlen=3)
print(deque2) # deque([2, 3, 4], maxlen=3)
deque1.extend(deque2)
# 直接在 deque1 上扩展,从尾部追加,会把头部元素挤出去,与元素是不是最早添加无关
print(deque1) # deque(['b', 'c', 2, 3, 4], maxlen=5)
# 等同于 appendleft,但如果超出最大长度,insert会报错,而 appendleft 不会
# deque2.insert(0, 1) # IndexError: deque already at its maximum size
deque2.remove(3)
print(deque2) # deque([2, 4], maxlen=3)
deque2.reverse()
print(deque2) # deque([4, 2], maxlen=3)
列表推导式
# 列表推导式也叫列表生成式,是通过代码来生成列表的一种方式,它比循环和map()函数要简洁
# 生成奇数列表
odd_list = []
# 通常的方式
for i in range(10):
if i % 2 == 1:
odd_list.append(i)
print(odd_list)
# 用列表生成式实现同样的功能,且性能高于列表
odd_list = [i for i in range(10) if i % 2 == 1]
print(odd_list)
# 逻辑复杂的需求
def handleItem(item):
if item % 2 == 1:
return item ** 2
else:
return item * 3
# 列表推导式会先执行 if i % 3 == 0 条件判断部分,再执行 for 前面的 handleItem()
odd_list = [handleItem(i) for i in range(10) if i % 3 == 0]
print(odd_list)
# 生成器表达式
odd_list = (i for i in range(10) if i % 3 == 0)
print(odd_list)
for i in odd_list:
print(i)
感谢支持
更多内容,请移步《超级个体》。