• 深挖 Python 元组 pt.2


    哈喽大家好,我是咸鱼

    在《深挖 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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    我们还可以执行反向操作(解包),将元组 point 的值解包为适当数量的变量

    >>> point = (7, 14, 21)
    
    >>> x, y, z = point
    >>> x
    7
    >>> y
    14
    >>> z
    21
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    可以看到,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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    解包最常见的用例就是利用解包在变量之间交换值

    没有解包的话使用常规赋值在两个变量之间交换值,必须使用临时变量

    >>> a = 200
    >>> b = 400
    
    >>> temp = a
    >>> a = b
    >>> b = temp
    
    >>> a
    400
    >>> b
    200
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    通过解包来交换变量值

    >>> a = 200
    >>> b = 400
    
    >>> a, b = b, a
    
    >>> a
    400
    >>> b
    200
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    解包还有一个用处是并行分配,比如说下面的操作:我们在进行赋值时需要知道对应的索引

    >>> employee = ("John Doe", 35, "Python Developer")
    
    >>> name = employee[0]
    >>> age = employee[1]
    >>> job = employee[2]
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如果通过解包来实现,则无需使用索引。这样能使代码更易阅读和理解,且不易出错

    >>> name, age, job = ("John Doe", 35, "Python Developer")
    
    • 1

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    关于这个操作符的更多用法,可以看我之前的文章:python 星号 * 还能这么用》

    函数返回元组

    在某些情况下,需要从函数返回多个值。所以可以构建一个带有逗号分隔的一系列参数的 return 语句,这样返回的是一个元组

    我们还可以使函数返回值是列表,这样需要我们显式地使用方括号来构建列表

    内置 divmod() 函数是返回多个值的函数的一个很好的例子。该函数接受两个数字,并在执行整数除法时返回一个包含商和余数的元组:

    >>> divmod(4, 2)
    (2, 0)
    
    # 由于该函数返回元组,因此可以使用解包语法将每个值存储在其专用变量中
    >>> quotient, remainder = divmod(8, 2)
    >>> quotient
    4
    >>> remainder
    0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    又或者我打算写一个函数,这个函数返回一组数的最大值和最小值

    >>> 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'>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    可以看到函数的返回值有两个:最大值、最小值。当用逗号分隔一系列值时,将创建一个元组。因此,此函数返回一个 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    可以看到,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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    可以看到,两个变量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'])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    可以看到,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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    可以看到,两个变量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'])
    
    • 1
    • 2
    • 3
    • 4
    • 5

    可以看到,对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'))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    需要注意的是,+ 左右两边必须都是元组,即只能将元组跟元组拼接。如果元组跟列表或其他对象拼接的话,会报错

    >>> (0, 1, 2, 3, 4, 5) + [6, 7, 8, 9]
    Traceback (most recent call last):
        ...
    TypeError: can only concatenate tuple (not "list") to tuple
    
    • 1
    • 2
    • 3
    • 4

    元组使用重复运算符 ( * )将元素克隆多次

    >>> 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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 元组反转和排序

    内置 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'
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    使用元组作为参数进行调用 reversed() 时,将获得一个迭代器对象,该对象以相反的顺序生成项

    如果要对元组进行排序,可以使用内置 sorted() 函数,该函数将值的可迭代对象作为参数并以列表形式返回排序后的值

    >>> numbers = (2, 9, 5, 1, 6)
    
    >>> sorted(numbers)
    [1, 2, 5, 6, 9]
    
    • 1
    • 2
    • 3
    • 4

    如果元组里面的元素数据类型不一致(异构数据),则无法排序

  • 相关阅读:
    VS2019自动闪退问题
    SQL语言概述与SQL语言的数据定义
    Unity Editor 编辑器介绍
    【BOOST C++ 20 设计模式】(1)库Boost.Flyweight
    代码随想录65——额外题目【二叉树】:129求根节点到叶节点数字之和、1382将二叉搜索树变平衡、100相同的树、116填充每个节点的下一个右侧节点指针
    图解KMP字符串匹配算法+代码实现
    SpringCloud Alibaba【一】简单介绍
    软件项目管理第一章---项目管理概念
    Kotlin或将超越Go?
    图像分割 - 阈值处理 - 多阈值处理(OTSU)
  • 原文地址:https://blog.csdn.net/s_alted/article/details/133737868