Python入门之列表和元组

        在python中,序列(sequence)是最基本的数据结构。序列中每个元素都有编号,即其位置或索引。索引从0开始。本节介绍最常用的2种python内置序列,:列表和元组。

        另一种重要的序列是字符串,已经介绍过。

        列表(List)和元组(Tuple)的主要不同在于:列表可以修改,元组不能

        python支持一种数据结构的基本概念,名为容器(container)。容器就是可包含其他对象的对象。两种主要的容器是序列(如列表和元组)和映射(如字典)。在序列中,每个元素都有编号,而在映射中,每个元素都有名称(也叫键)。映射将在后面的帖子介绍。有一种既不是序列也不是映射的容器,叫做集合(set),也会在后面讨论。

1. 通用的序列操作

        有几种操作适用于所有序列,包括索引、切片、相加、相乘和成员资格检查。python还提供了一些内置函数,可以确定序列的长度以及找出序列中最大和最小元素。

        有一个重要的操作放在后面章节介绍,它就是迭代。对序列进行迭代意味着对其每个元素都执行特定的操作。

1.1 索引

        索引(indexing)是从 0 开始递增的编号,负数表示从末尾开始计数。可以使用如下的编号来访问元素:

>>> s = "Python"
>>> s[0]      # 第一个字符
'P'
>>> s[3]      # 第四个字符
'h'
>>> s[-1]     # 最后一个字符
'n'
>>> s[-3]     # 倒数第三个字符
't'

        字符串就是由字符组成的序列,不同于其他一些语言,python没有专门用于表示字符的类型。因此一个字符就是只包含一个元素的字符串。

这种索引方式适用于所有序列。   

        如果函数返回调用一个序列,可对其直接执行索引操作。例如,如果你执行获取用户输入的年份的第四位,可以像下面这样做:

>>> fourth = input("Year:")[3]
Year:2025
>>> forth
'5'

练习1

        编写一个程序,提示用户输入年份、月份和日期(年、月、日),然后按如下格式输出日期:

Year: 1974
Month (1-12): 8
Day (1-31): 16
August 16th, 1974

其中 Day 的后缀要根据 1–31 正确显示 “st, nd, rd, th”。完整题解如下:

# 将以数字指定的年、月、日打印出来

# 1. 月份名称列表
months = [
    'January', 'February', 'March', 'April', 'May', 'June',
    'July', 'August', 'September', 'October', 'November', 'December'
]

# 2. 日期后缀列表:1st, 2nd, 3rd, 4th…31st
endings = ['st', 'nd', 'rd'] \
        + 17 * ['th'] \
        + ['st', 'nd', 'rd'] \
        + 7  * ['th'] \
        + ['st']

# 3. 读取输入
year  = input('Year: ')
month = input('Month (1-12): ')
day   = input('Day (1-31): ')

# 转换为整数,便于索引
month_number = int(month)
day_number   = int(day)

# 4. 获取对应的月份名称和后缀
month_name = months[month_number - 1]
suffix     = endings[day_number - 1]

# 5. 打印结果
print(f"{month_name} {day_number}{suffix}, {year}")

1.2 切片

        除了使用索引来访问单个元素外,还可以使用切片(slicing)来访问特定范围的元素。为此可使用2个索引,并用冒号分隔:

# 定义一个字符串
s = "abcdefg"

# 切片:从索引 2 开始(包含),到索引 5 结束(不包含)
# 也就是取 s[2], s[3], s[4]
slice_part = s[2:5]

print(slice_part)  # 输出 'cde'

        切片包含2个索引,其中第一个索引指定的元素包含在切片内,但是第二个索引指定的元素不包含在切片内。

        灵活运用索引可以减少很多工作量,例如:

负索引访问单个元素

        步长不能为0,否则无法向前移动,但可以为负数,即从右向左提取元素。

  • seq[-1]最后一个元素
  • seq[-3]:倒数第三个元素

取出或去掉末尾部分

  • seq[-N:]:取最后 N 个元素
  • seq[:-N]:去掉最后 N 个元素

复制、反转序列

  • seq[:]:复制整个序列(浅拷贝)
  • seq[::-1]:反转序列

改变步长切片

        执行切片操作时可以显示或隐式指定起点和终点,但通常忽略另一个参数,即步长。步长参数在未被指定时默认为1,如果显式指定步长,如下例的2,则意味着在起点和终点之间每隔一个元素提取一个元素。

  • seq[::2]:隔一个取一个(偶数索引)
  • seq[::-2]:从末尾开始,隔一个取一个

示例代码:

# 准备一个字符串和一个列表
s   = "abcdefg"
lst = [1, 2, 3, 4, 5, 6, 7, 8, 9]

print("原序列:", s, lst)

# 1. 负索引访问单个元素
print("最后一个:", s[-1], lst[-1])
print("倒数第三个:", s[-3], lst[-3])

# 2. 取出或去掉末尾部分
print("最后三位:", s[-3:], lst[-3:])
print("除去最后三位:", s[:-3], lst[:-3])

# 3. 复制、反转序列
print("复制序列:", s[:], lst[:])
print("反转序列:", s[::-1], lst[::-1])

# 4. 步长切片
print("隔一个取一个 (偶数位):", s[::2], lst[::2])
print("反向隔一个取一个:", s[::-2], lst[::-2])# 负的 step 时,start 默认改为序列末尾元素

1.3 序列相加

        使用加法运算符可以拼接序列。例如:

# 拼接相同类型的序列

# 示例1:拼接两个字符串
str1 = "Hello, "
str2 = "world!"
result_str = str1 + str2  # 字符串 + 字符串
print("拼接字符串结果:", result_str)

# 示例2:拼接两个列表
list1 = [1, 2, 3]
list2 = [4, 5, 6]
result_list = list1 + list2  # 列表 + 列表
print("拼接列表结果:", result_list)

# 示例3:拼接两个元组
tuple1 = (1, 2)
tuple2 = (3, 4)
result_tuple = tuple1 + tuple2  # 元组 + 元组
print("拼接元组结果:", result_tuple)

# 尝试拼接不同类型的序列
try:
    mixed_result = [1, 2, 3] + "abc"  # 列表 + 字符串
except TypeError as e:
    print("拼接不同类型的序列时出错:", e)

try:
    mixed_result = (1, 2) + [3, 4]  # 元组 + 列表
except TypeError as e:
    print("拼接不同类型的序列时出错:", e)

但是,拼接不同类型的序列通常不可以,例如列表和字符串。

1.4 乘法

        将序列与一个数字相乘,表示将重复这个序列x次来创建一个新的序列。例如:

# 示例1:字符串重复
string = "Hello "
multiplied_string = string * 3  # 将字符串重复3次
# 输出: 字符串重复结果: Hello Hello Hello 

# 示例2:列表重复
list_example = [1, 2, 3]
multiplied_list = list_example * 2  # 将列表重复2次
# 输出: 列表重复结果: [1, 2, 3, 1, 2, 3]

# 示例3:元组重复
tuple_example = (4, 5)
multiplied_tuple = tuple_example * 4  # 将元组重复4次
# 输出: 元组重复结果: (4, 5, 4, 5, 4, 5, 4, 5)

None、空列表和初始化

        空列表是使用不包含任何内容的2个方括号[]表示的。如果要创建一个可包含 10 个元素的列表,但没有任何有用的内容,可使用 [0] * 10,这将创建一个包含 10 个零的列表。有些情况下,可以使用表示“什么都没有”的值,如表示还没有在列表中添加任何内容。Python 中,None表示什么都没有。因此,要将列表的长度初始化为 10,可像下面这样做:        

>>> sequence = [None] * 10
>>> sequence
[None, None, None, None, None, None, None, None, None, None]

练习2 序列(字符串)乘法运算示例

# 在位于屏幕中央且宽度合适的方框内打印一个句子

sentence = input("Sentence: ")  # 用户输入句子

screen_width = 80  # 屏幕宽度
text_width = len(sentence)  # 句子的长度
box_width = text_width + 6  # 方框宽度(包括两侧的边框和填充)
left_margin = (screen_width - box_width) // 2  # 左侧边距,确保方框居中

# 打印方框
print()  # 换行
print(' ' * left_margin + '+' + '-' * (box_width - 2) + '+')  # 上边框
print(' ' * left_margin + '|' + ' ' * text_width + '|')       # 左右边框
print(' ' * left_margin + '|' + ' ' * 2 + sentence + ' ' * 2 + '|')  # 中间内容
print(' ' * left_margin + '|' + ' ' * text_width + '|')       # 左右边框
print(' ' * left_margin + '+' + '-' * (box_width - 2) + '+')  # 下边框
print()  # 换行

输出:

                                                                  
    +----------------------------------------+
    |                                        |
    |      He's a very naughty boy!          |
    |                                        |
    +----------------------------------------+
                                                                  

1.5 成员资格

        要检查特定的值是否包含在序列中,可以使用运算符in,它检查是否满足指定的条件,并返回相应的布尔运算符:满足时返回True,不满足时返回False。下面是一些in运算符的使用示例:

# 示例1:检查字符是否在字符串中
permissions = 'rw'
print('w' in permissions)  # 输出: True
print('x' in permissions)  # 输出: False

# 示例2:检查元素是否在列表中
users = ['mlh', 'foo', 'bar']
user_input = input('Enter your user name: ')
print(user_input in users)  # 根据输入判断用户是否存在

# 示例3:检查子字符串是否在字符串中
subject = '$$$ Get rich now!!! $$$'
print('$$$' in subject)  # 输出: True

练习3 序列成员资格示例

#检查用户名和PIN码

database = [
    ['albert', '1234'],
    ['dilbert', '4242'],
    ['smith', '7524'],
    ['jones', '9843']
]
username = input('User name:')
pin = input('PIN code')

if [username,pin] in database:
    print('Assess granted')

长度、最小值、最大值

        内置函数len、min、max 是非常有用的工具,分别用于获取序列的长度、最小值和最大值。以下是这些函数的基本用法及其应用场景。最后一个例子中,调用max和min时指定的实参并不是序列,而是直接将数值作为实参。

# 示例1:使用 len 函数获取序列的长度
numbers = [100, 34, 678]
print(len(numbers))  # 输出: 3

# 示例2:使用 max 函数获取序列中的最大值
print(max(numbers))  # 输出: 678

# 示例3:使用 min 函数获取序列中的最小值
print(min(numbers))  # 输出: 34

# 示例4:直接比较多个数值(非序列形式)
print(max(2, 3))     # 输出: 3
print(min(9, 3, 2, 5))  # 输出: 2

2.列表

2.1 创建列表:函数list

        使用函数list可以利用字符串创建列表:

>>> list('hello!')
['h', 'e', 'l', 'l', 'o', '!']#字符串转换为字符列表

 不仅字符串,任何序列都能作为list的参数。

        要将前述代码中的字符列表转换为字符串,可以使用’.join(somelist)表达式。

>>> str=''.join(a)
>>> str
'hello'

2.2基本的列表操作

2.2.1 给元素赋值

        使用索引表示法,给特定位置的元素赋值:

>>> x=[1,1,1]
>>> x[1]=2
>>> x
[1, 2, 1]

注:不能给不存在的元素赋值,即列表长度为2时,不能给索引为100的元素赋值。

2.2.2 删除元素

        del语句可以实现从列表中删除元素。

>>> del x[0]
>>> x
[2, 1]

2.2.3 给切片赋值

        切片是一个极其强大的功能,比如:

>>> name=list('Perl')
>>> name
['P', 'e', 'r', 'l']
>>> name[2:]=list('ar')
>>> name
['P', 'e', 'a', 'r']

 上述代码说明切片可同时给多个元素赋值,同时切片赋值还可以改变序列长度

>>> name=list('abcd')
>>> name
['a', 'b', 'c', 'd']
>>> name[:]=('python')
>>> name
['p', 'y', 't', 'h', 'o', 'n']

使用切片赋值还可以在不替换原有元素的情况下插入新元素:

>>> nums=[1,5]
>>> nums
[1, 5]
>>> nums[1:1]=[2,3,4]
>>> nums
[1, 2, 3, 4, 5]

上述代码替换了一个空切片,相当于插入了一个序列。可采取相反的措施来删除切片:

>>> nums=[1,2,3,4,5]
>>> nums
[1, 2, 3, 4, 5]
>>> nums[1:4]=[]
>>> nums
[1, 5]

上述代码与del nums[1,4]是等效的。

2.3 列表方法

        方法是与对象(列表、数、字符串等)联系紧密的函数。调用方法的格式是:

object.method(arguments)

方法调用与函数调用很像,只是在方法名前加上了对象和'.'。列表包含多个可以用来查看或修改其内容的方法:

1.append

        append方法将一个对象附加到列表末尾。

>>> lst=[1,2,3]
>>> lst.append(4)
>>> lst
[1, 2, 3, 4]

2.clear

        clear方法清空列表内容:

>>> lst
[1, 2, 3, 4]
>>> lst.clear()
>>> lst
[]

类似于切片赋值语句lst[:]=[]。

3.copy

        方法copy复制列表,这和常规复制不同,请看下面代码的区别:

>>> a=[1,2,3]
>>> b=a
>>> b[1]=4
>>> a
[1, 4, 3]#常规复制是将另一个名称关联到列表


#要让a和b指向不同的列表,就必须将b关联到a的副本
>>> a=[1,2,3]
>>> b=a.copy()
>>> b[1]=4
>>> a
[1, 2, 3]#b改变不再引起a变换

这类似于使用a[:]或list(a),它们也都复制a。

4.count

       方法count计算指定的元素在列表中出现了多少次:

>>> ['to','be','or','not','to','be'].count('to')
2
>>> x=[[1,2],1,1,[2,1,[1,2]]]
>>> x.count([1,2])
1
>>> x.count(1)
2

5.extend

方法extend能够同时将多个值附加到列表末尾,可将这些值组成的序列作为参数提供给方法extend。也就是说,可以使用一个列表来扩展另一个列表。

>>> a=[1,2,3]
>>> b=[4,5,6]
>>> a.extend(b)
>>> a
[1, 2, 3, 4, 5, 6]

虽然看起来和拼接很像,但是常规拼接的结果是返回一个新的序列,而非上述代码的被扩展序列(a)。

>>> a=[1,2,3]
>>> b=[4,5,6]
>>> a+b
[1, 2, 3, 4, 5, 6]
>>> a
[1, 2, 3]

6.index

        index方法在列表中查找指定值第一次出现的索引。

>>> knights=['we','are','the','knights','who','say','ni']
>>> knights.index('who')
4
>>> knights.indes('herring')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute 'indes'. Did you mean: 'index'?
#搜索'herring'时引发了异常,因为根本没有找到这个单词

7.insert

        方法insert用于将一个对象插入列表。

>>> nums=[1,2,3,4,5,6,7]
>>> nums.insert(3,'four')
>>> nums
[1, 2, 3, 'four', 4, 5, 6, 7]

8.pop

方法pop从列表中删除末尾最后一个元素,并返回这个元素。

>>> x=[1,2,3]
>>> x.pop()
3
>>> x
[1, 2]
>>> x.pop(0)
1
>>> x
[2]

注:pop是唯一既修改列表又返回一个非None值的列表方法。

        使用pop可实现栈(stack)。

        push和pop是栈的2种常见操作方式,python没有提供push。但可以用append来代替:

>>> x=[1,2,3]
>>> x.append(x.pop())
>>> x
[1, 2, 3]

9.remove

        方法remove用于删除第一个为指定值的元素。

>>> x=['to','be','or','not','to','be']
>>> x.remove('be')
>>> x
['to', 'or', 'not', 'to', 'be']
>>> x.remove('bee')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
#无法删除列表中其他为指定值的元素

10.reverse

        方法reverse按相反的顺序排列列表中的元素:

>>> x=[1,2,3]
>>> x.reverse()
>>> x
[3, 2, 1]

        如果要按照相反的顺序迭代序列,可使用函数reversed。这个函数不返回列表,而是返回一个迭代器(后面章节介绍):

[3, 2, 1]
>>> list(reversed(x))
[1, 2, 3]

11.sort和sorted

        方法sort用于对原列表排序,返回任何值:

>>> x=[4,6,2,1,7]
>>> x.sort()
>>> x
[1, 2, 4, 6, 7]

因此,一种常见的错误写法是这样的:

>>> x=[4,6,2,1,7]
>>> y=x.sort()
>>> print(y)
None

正确写法是利用上面的copy方法,将y关联到x的副本,再对y进行排序:

>>> x=[4,6,2,1,7]
>>> y=x.copy()
>>> y.sort()
>>> x
[4, 6, 2, 1, 7]
>>> y
[1, 2, 4, 6, 7]

另一种方式是使用sorted函数

>>> x=[4,6,2,1,7]
>>> y=sorted(x)
>>> y
[1, 2, 4, 6, 7]
>>> x
[4, 6, 2, 1, 7]

12.高级排序

        方法sort接受2个可选参数:key和reserve。参数key接收一个函数,对序列中的每个元素调用该函数,返回值作为排序的依据,比如:

# 示例 1:按字符串长度升序排序(key=len)
words = ['aardvark', 'abalone', 'acme', 'add', 'aerate']
# key=len 表示:排序时,先调用 len(x) 得到每个元素的“键”(这里是字符串长度),
# 然后根据这个键来决定先后顺序
words.sort(key=len)
print(words)
# 输出:['add', 'acme', 'aerate', 'abalone', 'aardvark']
# 解释:'add' 长度 3,'acme'、'aerate' 长度 4,...,最长的是 'aardvark'

# 示例 2:按数值绝对值升序排序(key=abs)
nums = [4, -1, -7, 3, -2]
# key=abs 表示:排序时先用 abs(x) 计算每个元素的绝对值,再按绝对值排序
nums.sort(key=abs)
print(nums)
# 输出:[-1, -2, 3, 4, -7]
# 解释:abs(-1)=1,abs(-2)=2,abs(3)=3,abs(4)=4,abs(-7)=7,按 1,2,3,4,7 排序

        参数reverse可以指定为真值True或False,指出是否要按照相反(默认是升序)的顺序对列表进行排序。

# 示例 3:按数值降序排序(reverse=True)
nums = [4, 6, 2, 1, 7, 9]
# reverse=True 表示:在完成默认(或 key 指定)的升序排序后,再把整个列表反转
nums.sort(reverse=True)
print(nums)
# 输出:[9, 7, 6, 4, 2, 1]

        函数sorted()也接受参数key和reverse,与sort方法不同的是,它会返回一个新列表,且不改变原列表:

# 示例 1:基本用法,不带 key 和 reverse
data = [5, 2, 9, 1]
print(sorted(data))  # 输出 [1, 2, 5, 9]
print(data)          # 原列表依旧 [5, 2, 9, 1]

# 示例 2:按绝对值升序排序(key=abs)
nums = [4, -1, -7, 3, -2]
sorted_nums = sorted(nums, key=abs)
print(sorted_nums)   # 输出 [-1, -2, 3, 4, -7]
# 解释:先计算 abs(x),得到 1,2,3,4,7,再按这些值升序排列

# 示例 3:按字符串长度降序排序(key=len, reverse=True)
words = ['banana', 'pie', 'Washington', 'book']
sorted_words = sorted(words, key=len, reverse=True)
print(sorted_words)  # 输出 ['Washington', 'banana', 'book', 'pie']
# 解释:先按 len(x) 排序,再整体翻转,最长的排在最前面

3.元组

3.1 创建元组

        元组是不可修改的序列,常用于表示固定的数据集合。只需用逗号分隔开值就能自动创建一个元组。

>>> 1,2,3
(1, 2, 3)
>>> x=1,2,3
>>> x
(1, 2, 3)

        决定生成元组的其实是逗号而是圆括号。 圆括号是可选的,生成空元组或需要避免语法歧义的情况除外。 例如,f(a, b, c) 是在调用函数时附带三个参数,而 f((a, b, c)) 则是在调用函数时附带一个三元组。

        元组还可以用圆括号括起,这也是通常采用的做法:

>>> (1,2,3)
(1, 2, 3)

        空元组用2个空括号表示:

>>> ()
()

        表示只包含一个值的元组,就是在它后面加上逗号。下面的代码示例中,最后2个示例创建的元组长度为1,而第一个示例根本没有创建元组。:

>>> 42
42
>>> 42,
(42,)
>>> (42,)
(42,)

        创建多个相同值的元组可以用乘法(不要忽略逗号):

>>> 3*(40+2,)
(42, 42, 42)

3.2 tuple函数

        函数tuple的工作原理与list相似,将一个序列作为参数,并将其转换为元组。如果参数已经是元组,就原封不动返回它。

>>> tuple([1,2,3])
(1, 2, 3)
>>> tuple('abc')
('a', 'b', 'c')
>>> tuple((1,2,3))
(1, 2, 3)

        元组并不复杂,除了创建和访问其元素外,对元组的可执行操作不多。元组的访问和其他序列相同:

>>> x=1,2,3
>>> x
(1, 2, 3)
>>> x[1]
2
>>> x[:2]
(1, 2)

        元组的切片也是元组。它们通常用作映射中的键以及集合的成员,而列表则不行。映射将在以后介绍。

练习题:列表合并

已知两个列表 ab,其中 a 包含 20 个元素,b 包含 30 个元素。请编写 Python 代码创建一个新的列表 c,使其满足:

1.c 的前 20 个元素来自列表 a

2.c 的后 30 个元素来自列表 b

法1:直接赋值

a = list(range(1, 21))  # 示例:a 具有 20 个元素
b = list(range(21, 51))  # 示例:b 具有 30 个元素

c = [0] * (20 + 30)  # 预先创建一个长度为50的列表
for i in range(20):
    c[i] = a[i]
for i in range(30):
    c[i + 20] = b[i]

print("直接赋值法合并结果:", c)

 法2:使用 append() 方法

# 定义两个列表
a = list(range(1, 21))  # 示例:a 具有 20 个元素 [1, 2, ..., 20]
b = list(range(21, 51))  # 示例:b 具有 30 个元素 [21, 22, ..., 50]

# 创建空列表并逐个添加元素
c = []
for item in a:
    c.append(item)
for item in b:
    c.append(item)

print("append() 方法合并结果:", c)

法3:使用 + 操作符

# 定义两个列表
a = list(range(1, 21))  # 示例:a 具有 20 个元素 [1, 2, ..., 20]
b = list(range(21, 51))  # 示例:b 具有 30 个元素 [21, 22, ..., 50]

# 直接使用 + 操作符合并
c = a + b  

print("+ 操作符合并结果:", c)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值