哈喽大家好,我是咸鱼
在《深挖 Python 元组 pt.1》中我们了解 Python 元组的一些概念(索引和切片等),以及如何创建元组,最重要的是我们还介绍了元组的不可变特性
那么今天我们来继续深挖 Python 元组
打包&解包
在 python 中,元组可以被打包(packing )和解包(unpacking )
例如,point = x, y, z
将会把 x, y 和 z 的值打包到 point 中,于是创建了一个新元组
>>> x = 1
>>> y = 2
>>> z = 3
>>> point = x,y,z
>>> point
(1, 2, 3)
我们还可以执行反向操作(解包),将元组 point
的值解包为适当数量的变量
>>> point = (7, 14, 21)
>>> x, y, z = point
>>> x
7
>>> y
14
>>> z
21
可以看到,x, y, z = point
神奇地将point的内容解压缩为三个变量。注意,值按顺序转到变量。(第一个值归第一个变量,第二个值归第二个变量,依此类推)
Python 3.5 扩展了元组解包语法,以处理各种可迭代对象
所以不单单元组可以解包,可迭代对象也可以解包
在常规解包中,变量的数量必须与要解包的值的数量相匹配。否则会报错
>>> point = (7, 14, 21)
>>> x, y = point
Traceback (most recent call last):
...
ValueError: too many values to unpack (expected 2)
解包最常见的用例就是利用解包在变量之间交换值
没有解包的话使用常规赋值在两个变量之间交换值,必须使用临时变量
>>> a = 200
>>> b = 400
>>> temp = a
>>> a = b
>>> b = temp
>>> a
400
>>> b
200
通过解包来交换变量值
>>> a = 200
>>> b = 400
>>> a, b = b, a
>>> a
400
>>> b
200
解包还有一个用处是并行分配,比如说下面的操作:我们在进行赋值时需要知道对应的索引
>>> employee = ("John Doe", 35, "Python Developer")
>>> name = employee[0]
>>> age = employee[1]
>>> job = employee[2]
如果通过解包来实现,则无需使用索引。这样能使代码更易阅读和理解,且不易出错
>>> name, age, job = ("John Doe", 35, "Python Developer")
Python 还有一个打包和解包运算符 ( *
),我们可以使用它让解包(打包)操作更加灵活
例如当左边的变量数和右边元组的元素数量不匹配时,可以使用 *
使得单个变量接收多个值
>>> numbers = (1, 2, 3, 4, 5)
>>> *head, last = numbers
>>> head
[1, 2, 3, 4]
>>> last
5
>>> first, *middle, last = numbers
>>> first
1
>>> middle
[2, 3, 4]
>>> last
5
>>> first, second, *tail = numbers
>>> first
1
>>> second
2
>>> tail
[3, 4, 5]
>>> first, *_ = numbers
>>> first
1
关于这个操作符的更多用法,可以看我之前的文章:《python 星号 * 还能这么用》
函数返回元组
在某些情况下,需要从函数返回多个值。所以可以构建一个带有逗号分隔的一系列参数的 return 语句,这样返回的是一个元组
我们还可以使函数返回值是列表,这样需要我们显式地使用方括号来构建列表
内置 divmod()
函数是返回多个值的函数的一个很好的例子。该函数接受两个数字,并在执行整数除法时返回一个包含商和余数的元组:
>>> divmod(4, 2)
(2, 0)
# 由于该函数返回元组,因此可以使用解包语法将每个值存储在其专用变量中
>>> quotient, remainder = divmod(8, 2)
>>> quotient
4
>>> remainder
0
又或者我打算写一个函数,这个函数返回一组数的最大值和最小值
>>> def find_extremes(iterable):
... data = tuple(iterable)
... if len(data) == 0:
... raise ValueError("input iterable must not be empty")
... return min(data), max(data)
...
>>> extremes = find_extremes([3, 4, 2, 6, 7, 1, 9])
>>> extremes
(1, 9)
>>> type(extremes)
<class 'tuple'>
可以看到函数的返回值有两个:最大值、最小值。当用逗号分隔一系列值时,将创建一个元组。因此,此函数返回一个 tuple
对象
深拷贝浅拷贝
当需要转换数据时,通常需要复制对象,同时保持原始数据不变。在处理可变数据类型(如列表和字典)时,副本非常有用
副本可以在不影响原数据的情况下对数据进行更改
- 直接赋值
我们先来看一个例子
>>> student_info = ("Linda", 18, ["Math", "Physics", "History"])
>>> student_profile = student_info[:]
>>> id(student_info) == id(student_profile)
True
>>> id(student_info[0]) == id(student_profile[0])
True
>>> id(student_info[1]) == id(student_profile[1])
True
>>> id(student_info[2]) == id(student_profile[2])
True
可以看到,student_info、 student_profile
是对同一元组对象的引用。所以, student_profile
是 的 student_info
别名而不是副本
- 浅拷贝
copy
模块中的 copy()
函数生成等效结果
>>> from copy import copy
>>> student_info = ("Linda", 18, ["Math", "Physics", "History"])
>>> student_profile = copy(student_info)
>>> id(student_info) == id(student_profile)
True
>>> id(student_info[0]) == id(student_profile[0])
True
>>> id(student_info[1]) == id(student_profile[1])
True
>>> id(student_info[2]) == id(student_profile[2])
True
可以看到,两个变量student_info、 student_profile
都包含对同一元组对象和相同元素的引用
上面的元组里面包含了一个列表元素,我们知道列表是可变的,我们来试着更改一下
>>> student_profile[2][2] = "Computer science"
>>> student_profile
('Linda', 18, ['Math', 'Physics', 'Computer science'])
>>> student_info
('Linda', 18, ['Math', 'Physics', 'Computer science'])
可以看到,student_profile
更改会影响 student_info
中的原始数据
- 深拷贝
下面的例子中, student_info
通过 deepcopy()
函数制作了student_profile
>>> from copy import deepcopy
>>> student_info = ("Linda", 18, ["Math", "Physics", "History"])
>>> student_profile = deepcopy(student_info)
>>> id(student_info) == id(student_profile)
False
>>> id(student_info[0]) == id(student_profile[0])
True
>>> id(student_info[1]) == id(student_profile[1])
True
>>> id(student_info[2]) == id(student_profile[2])
False
可以看到,两个变量student_info、 student_profile
指向的元组对象不是同一个
如果我们对里面的列表元素进行更改
>>> student_profile[2][2] = "Computer science"
>>> student_profile
('Linda', 18, ['Math', 'Physics', 'Computer science'])
>>> student_info
('Linda', 18, ['Math', 'Physics', 'History'])
可以看到,对student_profile
的修改不会影响 student_info
中的数据
总结一下:
- 元组的浅拷贝不会创建一个新的对象(副本)。
-
元组的深拷贝创建一个新的元组对象
- 对于元组内的不可变元素,它们仍然会共享相同的内存地址
- 对于元组内的可变元素,则是创建了一个新的对象,不共享内存地址
其他操作
- 元组拼接和重复
在 Python 中连接两个元组,可以使用加号运算符 ( +
)
>>> personal_info = ("John", 35)
>>> professional_info = ("Computer science", ("Python", "Django", "Flask"))
>>> profile = personal_info + professional_info
>>> profile
('John', 35, 'Computer science', ('Python', 'Django', 'Flask'))
需要注意的是,+
左右两边必须都是元组,即只能将元组跟元组拼接。如果元组跟列表或其他对象拼接的话,会报错
>>> (0, 1, 2, 3, 4, 5) + [6, 7, 8, 9]
Traceback (most recent call last):
...
TypeError: can only concatenate tuple (not "list") to tuple
元组使用重复运算符 ( *
)将元素克隆多次
>>> numbers = (1, 2, 3)
>>> numbers * 3
(1, 2, 3, 1, 2, 3, 1, 2, 3)
>>> 4 * numbers
(1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3)
- 元组反转和排序
内置 reversed()
函数将序列作为参数,并返回一个迭代器,该迭代器以相反的顺序从输入序列生成值
>>> days = (
... "Monday",
... "Tuesday",
... "Wednesday",
... "Thursday",
... "Friday",
... "Saturday",
... "Sunday",
... )
>>> reversed(days)
<reversed object at 0x107032b90>
>>> tuple(reversed(days))
(
'Sunday',
'Saturday',
'Friday',
'Thursday',
'Wednesday',
'Tuesday',
'Monday'
)
使用元组作为参数进行调用 reversed()
时,将获得一个迭代器对象,该对象以相反的顺序生成项
如果要对元组进行排序,可以使用内置 sorted()
函数,该函数将值的可迭代对象作为参数并以列表形式返回排序后的值文章来源:https://www.toymoban.com/news/detail-710729.html
>>> numbers = (2, 9, 5, 1, 6)
>>> sorted(numbers)
[1, 2, 5, 6, 9]
如果元组里面的元素数据类型不一致(异构数据),则无法排序文章来源地址https://www.toymoban.com/news/detail-710729.html
到了这里,关于深挖 Python 元组 pt.2的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!