10. 数据类型 - 元组详解

更新於 發佈於 閱讀時間約 14 分鐘
raw-image

Hi,大家好。我是茶桁。

之前两节分别介绍了字符串和列表,今天,我们来讲讲另外一个常用到的数据类型:元组。

元组和列表很像,两者都是一组有序的数据的组合。但是也有很多不同点,比如元组内的元素一旦定义了就不可以再修改,因此元组称为不可变数据类型

元组定义

元组的定义方式包括以下要点:

  • 定义元组变量 = (), 或者变量 = tuple()
  • 可以使用变量 = (*iterable)定义含有数据的元组
  • ⚠️ 需要注意:如果元组中只有一个元素时,这唯一的元素后面也必须加逗号,这是为了区分其他元素标识这是一个元组: (1,)
  • 特例: 变量 = 1,2,3, 这种方式也可以定义为一个元组。

元组的相关操作

由于元组是一个不可变的数据类型,因此其在创建之后只能使用索引进行访问,无法进行其他操作。访问方式其实和列表一样,同样可以使用切片方式获取元素。

元组可以进行切片操作,在访问数据这件事情上和列表几乎一样,没有什么区别,所以完全可以借鉴上一节我讲的内容来看,这里就不详细介绍了,仅仅给大家写出一些案例:

# 常见的元组切片索引查询操作
tup = 1, 2, 3, 4, 5, 5, 4, 3, 2, 1
print('[:]:\t',tup[:]) # 获取全部
print('[::]:\t', tup[::]) # 获取全部
print('[1:]:\t', tup[1:]) # 从索引1开始获取到最后
print('[1:3]:\t', tup[1:3]) # 从索引1开始索引到3之前
print('[:3]:\t', tup[:3]) # 从0开始索引到3之前
print('[1:5:2]:\t', tup[1:5:2]) # 从1开始索引到5之前,步进值为2
print('[::2]:\t', tup[::2]) #从0开始索引到最后,步进值为2
print('[5:1:-1]:\t', tup[5:1:-1]) # 从5开始往前索引到1, 步进值为-1

---
[:]: (1, 2, 3, 4, 5, 5, 4, 3, 2, 1)
[::]: (1, 2, 3, 4, 5, 5, 4, 3, 2, 1)
[1:]: (2, 3, 4, 5, 5, 4, 3, 2, 1)
[1:3]: (2, 3)
[:3]: (1, 2, 3)
[1:5:2]: (2, 4)
[::2]: (1, 3, 5, 4, 2)
[5:1:-1]: (5, 5, 4, 3)

除了常用的切片操作之外,和列表一样,元组也能使用一些基本函数来完成查询操作

# 获取元组的长度
print(len(tup))

# 统计一个元素在元组中出现的次数
print(tup.count(5))

# 获取一个元素在元组内的下标(索引值)
print(tup.index(5, 1, 9))

---
10
2
4

除此之外,元组还可以引用基础的数学运算符+*来进行加和乘的运算。

# 加和乘操作
print((1, 2, 3) + ('a', 'b'))
print((1, 2, 3) * 5)

---
(1, 2, 3, 'a', 'b')
(1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3)

有一个同学曾经问过我:既然元组是不可修改的,那为什么还能用加和乘的运算呢?

不知道在座的小伙伴有没有这种想法?

这样吧,我重新写一段代码,小伙伴们应该就明白了:

# 元组是这样的
tup = (1, 2, 3, 4)
print(id(tup))
tup2 = (3, 4, 5, 6)
tup = tup + tup2
print(id(tup))

---
4446580864
4450629120
4450647984
4459297216

不知道大家看明白没有。解释一下,其实就是说,在进行加法和乘法运算的时候,即便我们的变量名是一样的,实际上也是生成了一个新的元组,而不是之前那一个了。所以这个并非是修改和更新,而是创建。

为了对比,我再写一段更新的代码给大家看:

# 尝试更新元组
tup = (1, 2, 3, 4, 5, 6)
print(tup[2])
del tup[2]
print(tup)

---
TypeError: 'tuple' object doesn't support item deletion

可以看到,报错提示了,tuple对象不支持删除项目

作为对比,我们看看列表的:

# 看看列表更新(只看id)
items = [1, 2, 3, 4, 5, 6]
print(id(items))
del items[2]
print(id(items), '\t',items)

---
4450415936
4450415936 [1, 2, 4, 5, 6]

可以看到,不仅是内部元素被删除了,并且id完全没有变化。也就是说,我们是在这个列表本身做了删除动作,并未生成新的列表。关于这部分,我们上一节中的深拷贝和浅拷贝讲的很清楚,大家可以回去好好看看理解一下。

元组推导式 生成器

在起初,我们先来看看元组是否和列表一样支持使用推导式。

tup = (i for i in range(10))
print(tup)

---
<generator object <genexpr> at 0x109cfa570>

这段并非是报错,而是打印出了tup的类型:生成器对象。

我们之前学过,使用列表推导式生成的结果是一个列表,但是元组似乎和列表并不一样,生成的结果是一个生成器对象。

列表推导式 ==> [变量运算 for i in 容器]  ==> 结果 是一个 列表
元组推导式 ==> (变量运算 for i in 容器) ==> 结果 是一个 生成器

那这里就有个疑问了,什么是生成器?

生成器是一个特殊的迭代器,生成器可以自定义,也可以使用元组推导式去定义。

生成器是按照某种算法去推算下一个数据或结果,只需要往内存中存储一个生成器,节约内存消耗,提升性能。

语法

  1. 里面是推导式,外面是一个()的结果就是一个生成器
  2. 自定义生成器,含有yield关键字的函数就是生成器

那么,我们到底应该怎样操作生成器呢?

既然生成器是迭代器的一种,那我们是否可以使用迭代器的操作方法来操作生成器呢?

说干就干,让我们直接操作做实验:

tup = (i for i in range(10))
print(next(tup))
print(next(tup))
print(next(tup))
print(list(tup))

---
0
1
2
[3, 4, 5, 6, 7, 8, 9]

没毛病,确实支持next()函数,并且内部元素在使用后也被移除了。

# 让我们将其转为元组
print(tuple(tup))

---
()

哎,为什么里面是空的?那是因为,我们上一段代码中的最后一句,已经讲所有迭代器内的元素转为了列表,素衣目前迭代器tup内是没有任何元素了,所以我们转过来必须是空的。

再来生成一个,我们来试试用for对它进行循环:

tup = (i for i in range(10))
for i in tup:
print(i, end=" ")

---
0 1 2 3 4 5 6 7 8 9

可以看到结果没有问题。可以推断出,生成器和迭代器没有任何区别,我们在平时使用的时候,就直接将它作为迭代器使用就可以了。

yield关键字

在之前,我们提到了,含有yield关键字的函数就是生成器。

它返回的结果是一个迭代器。我们可以理解为,生成器函数就是一个返回迭代器的函数。

那么yield有哪些需要注意的点呢?我们先在下面列一下,之后再带着大家一起过:

yield和函数中的return有点像

  • 共同点: 执行到这个关键字后会把结果返回来
  • 不同点:
    • return会把结果返回,并结束当前函数的调用
    • yield会返回结果,并记住当前代码执行的位置,下一次调用时会从上一次离开的位置继续向下执行。

上实验:

# 定义一个普通函数
def func():
print('Hello yield')
return 'yield'
print('Hello again')

func()
func()

---
Hello yield
Hello yield

在这个自定义函数内,return执行的时候,就会结束当前函数的调用,而在之前,第一个print()函数正确执行了,但是第二个print()函数因为在return之后,所以并未运行。即便我们一共执行了两次函数,可是也仅仅是讲第一个print()函数执行了两次。

# 尝试使用yield定义一个生成器函数
def func():
print('Hello yield')
yield 'yield'
print('Hello again')
yield 'again'

# 调用生成器函数, 返回一个迭代器
res = func()
next(res)
next(res)

---
Hello yield
Hello again

可以看到,当我们使用next()函数的时候,迭代器起作用了,每执行一次,分别调用第一个yield之前和之后的print(),也就是说继续执行了。

那如何验证yield的返回呢?我们将这段代码改造一下:

# 尝试使用yield定义一个生成器函数
def func():
print('Hello yield')
yield 'return yield'
print('Hello again')
yield 'return again'

# 调用生成器函数, 返回一个迭代器
res = func()
str = next(res)
print(str)
str = next(res)
print(str)

---
Hello yield
return yield
Hello again
return again

没问题,依次打印出了返回值return yieldreturn again

还记得我们之前教过,使用list函数去调用,可以讲迭代器的返回结果,作为容器的元素,让我们再来改造一下这段代码:

# 尝试使用yield定义一个生成器函数
def func():
print('Hello yield')
yield 'return yield'
print('Hello again')
yield 'return again'

# 调用生成器函数, 返回一个迭代器
res = func()

items = list(res)
print(items)

---
Hello yield
Hello again
['return yield', 'return again']

我们看见,确实,返回结果被依次放入了一个list容器中。

当然,除了list函数之外,还可以使用for来获取迭代器内容:

# 尝试使用yield定义一个生成器函数
def func():
print('Hello yield')
yield 'return yield'
print('Hello again')
yield 'return again'

# 调用生成器函数, 返回一个迭代器
res = func()

items = []
for i in res:
items.append(i)

print(items)

---
Hello yield
Hello again
['return yield', 'return again']

我们来分析一下在以上这几段代码中,生成器函数调用时到底是什么过程。

首先,调用生成器函数,返回一个迭代器:

  1. 第一次去调用迭代器,走到当前的生成器函数中,遇到第一个yield, 把return yield返回,并且记住当前的执行状态(位置),暂停了执行,等待下一次的调用
  2. 第二次去调用迭代器,从上一次遇到的yield位置开始执行,遇到了第二个yield,把return again返回,并重新记录状态,暂停执行,等待下一次调用。
  3. 如果最后又调用了迭代器,那么会从上一次的yield位置开始,可是后面没有了,就会超出范围,抛出异常:StopIteration:

那么这种一次一次调用执行的方式什么时候适用呢?比如说,我们在处理一个非常大的数据,电脑可能吃不住,这个时候我们就可以拆开来一次一次的执行获取结果。

小练习

  1. 为了能达到练习的目的,从这一节开始,所有练习可以不在课程中展示了。大家先做一下,然后可以在我下一节课中的源码中去找答案,然后来看看和自己做的是否一样。
  2. 以下所有练习必须使用列表推导式来实现
  3. 有些练习不止一个方法,大家尝试用多种方法来实现一下。
  4. 做完的小伙伴可以在课程后面留言讨论。
  5. 上一节的练习已经放到本次教程的源码内,可以在此获取:https://github.com/hivandu/AI_Cheats/tree/main/Python
今天就一个练习:使用生成器改写斐波那契数列函数
avatar-img
9會員
62內容數
从基础开始,再到Python,然后是CV、BI、NLP等相关技术。从头到尾详细的教授一边人工智能。
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
茶桁的沙龍 的其他內容
Hi,大家好。我是茶桁。 最近几节课,我们都是在详细讲解Python内的数据类型,上一节课我们详细了解了字符串,这节课,让我们来详解一下列表。 首先,我们先有一个大的概念,列表,其实就是一组有序的数据组合;另外,列表中的数据是可以被修改的。也就是说,列表是一个可变序列类型。 列表定义 如何在
Hi, 大家好。我是茶桁。 前几节课中我们学习了函数,那么这节课开始,我们花几节课返过头来详细的学习一下Python内的数据类型。第一节课,让我们先从字符串开始: 回顾字符串的定义方式 了解转义字符 字符串格式化的方法 字符串相关函数 字符串的定义方式 单引号定义字符串 ‘ ’ 双引
Hi,大家好。我是茶桁。 讲完了基础函数和高阶函数之后,我们这一节来研究下Python的内置函数,看看Python在安装完毕之后的解释器里,到底都预先给我们提供好了哪些可用的函数。 本节内容着重介绍一些常用函数,并且会做一些应用上的示例。当然,对于Python的内置函数,我们还可以查询官方文档,
Hi,大家好。 我是茶桁。 本节课,我们来学习一下Python中的「高阶函数」。 让我们先来了解一下,什么是递归函数。 递归函数就是定义一个函数,然后在此函数内,自己调用自己。 既然是自己调用自己,那这个函数必须要有一个结束才行,否则会一直重复的调用下去,直到调用层数越来越多,最
HI, 大家好。我是茶桁。 上一节中我们学习了Python基本的流程控制,并且预告了这一节的内容,就是将要学习「模块化编程」。那什么是模块化编程呢?按照维基百科的说法: 模块化编程(英语:modular programming),是强调将计算机程序的功能分离成独立的、可相互改变的“模块)”(mo
Hi,大家好。我是茶桁。 在前面几节课的基础之上,我们今天开始尝试在Python中控制流程。这中间,让我们来做一些实际的练习。 Python语句的分类 让我们先了解一下Python语句的分类。 在Python中,可分为单行代码和代码块/组, 顾名思义,单行代码就是一行的Python代码,而代
Hi,大家好。我是茶桁。 最近几节课,我们都是在详细讲解Python内的数据类型,上一节课我们详细了解了字符串,这节课,让我们来详解一下列表。 首先,我们先有一个大的概念,列表,其实就是一组有序的数据组合;另外,列表中的数据是可以被修改的。也就是说,列表是一个可变序列类型。 列表定义 如何在
Hi, 大家好。我是茶桁。 前几节课中我们学习了函数,那么这节课开始,我们花几节课返过头来详细的学习一下Python内的数据类型。第一节课,让我们先从字符串开始: 回顾字符串的定义方式 了解转义字符 字符串格式化的方法 字符串相关函数 字符串的定义方式 单引号定义字符串 ‘ ’ 双引
Hi,大家好。我是茶桁。 讲完了基础函数和高阶函数之后,我们这一节来研究下Python的内置函数,看看Python在安装完毕之后的解释器里,到底都预先给我们提供好了哪些可用的函数。 本节内容着重介绍一些常用函数,并且会做一些应用上的示例。当然,对于Python的内置函数,我们还可以查询官方文档,
Hi,大家好。 我是茶桁。 本节课,我们来学习一下Python中的「高阶函数」。 让我们先来了解一下,什么是递归函数。 递归函数就是定义一个函数,然后在此函数内,自己调用自己。 既然是自己调用自己,那这个函数必须要有一个结束才行,否则会一直重复的调用下去,直到调用层数越来越多,最
HI, 大家好。我是茶桁。 上一节中我们学习了Python基本的流程控制,并且预告了这一节的内容,就是将要学习「模块化编程」。那什么是模块化编程呢?按照维基百科的说法: 模块化编程(英语:modular programming),是强调将计算机程序的功能分离成独立的、可相互改变的“模块)”(mo
Hi,大家好。我是茶桁。 在前面几节课的基础之上,我们今天开始尝试在Python中控制流程。这中间,让我们来做一些实际的练习。 Python语句的分类 让我们先了解一下Python语句的分类。 在Python中,可分为单行代码和代码块/组, 顾名思义,单行代码就是一行的Python代码,而代
你可能也想看
Google News 追蹤
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
在這篇Python筆記中,我們將探索如何使用元組來管理地理資訊數據。元組是一種有序且不可變的資料結構,非常適合用於儲存地理座標等固定不變的資料。通過實際的Coding Example,我們將展示如何定義元組、進行解包操作,並如何利用元組來簡化地理資訊的管理和處理。
Thumbnail
本篇文章探討了Python中的字串、列表、元組、集合與字典這五種資料類型的定義與基本操作。這些資料類型各具特點,例如字串和元組是不可變的,列表和集合是可變的,適合不同的使用場景。文章中詳細介紹如何定義進行基本的操作(如添加、刪除、訪問元素等)。
Thumbnail
在資料結構與演算法裡, 最簡單的線性資料結構除了list之外就是linked list鏈結串列了。 Linked list又有分為單向Singly linked list 和雙向Doubly linked list 在這篇文章,會從最基礎的Singly linked list開始講起。 定義
Thumbnail
在 Python 中,tuple 與 List有一個關鍵的不同點:tuple 是不可變的,這意味著一旦創建了 tuple,就無法更改其內容。 這與 List的可變性形成了對比,list 可以新增、刪除或修改元素。 元素的意思: 元素:指的是 List 中的每一個獨立的項目或值。
Thumbnail
今天要來介紹的是Python中資料型別的函數, 這幾天學習的素材是Youtube上“程式柴大大的Python 6 小時初學者課程”,一步一步帶著大家操作並解,學習中也別忘了要多多練習,練習的部分我是把我學到的東西請Chatgpt幫我出類似的題型並讓我練習。 以下我先寫出一個簡單的code,再加以
Thumbnail
Array可以說是各種語言除了基本型別之外,最常用的資料型別與容器之一了。 Array 這種連續格子狀的資料結構,在Python要怎麼表達呢? 建立一個空的陣列 最簡單也最直接的寫法就是 array = [] # Python list [] 就對應到大家熟知的array 陣列型態的資料結
Thumbnail
給定一個輸入陣列,每一個tuple代表節點之間了從屬關係。 請從從屬關係重建整顆二元樹,並且返回整顆二元樹的根結點。
Thumbnail
本文詳細介紹了Python中的各種資料型別,包括整數、字串、清單、元組、集合和字典,並提供了相關的操作範例。此外,還解釋了如何在Python中定義和操作變數,包括如何同時對多個變數進行賦值。
Thumbnail
本文介紹了串列運算式的應用,以及與Lambda匿名函式方法的比較,並提供了程式範例。串列運算式提供了一種簡潔的語法,用於創建、轉換和過濾列表。lambda函式用於創建匿名函式,通常用於簡單的操作。建議在比較複雜的情況下使用一般for迴圈加if來表示。
Thumbnail
列表(List)和元組(Tuple)都是 Python 中用來存儲集合元素的數據結構,兩者看起來很像,在初學時很容易搞混,所以觀念要建立好。 可以把列表(List)和元組(Tuple)想像成是一個容器,什麼元素都可以塞
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
在這篇Python筆記中,我們將探索如何使用元組來管理地理資訊數據。元組是一種有序且不可變的資料結構,非常適合用於儲存地理座標等固定不變的資料。通過實際的Coding Example,我們將展示如何定義元組、進行解包操作,並如何利用元組來簡化地理資訊的管理和處理。
Thumbnail
本篇文章探討了Python中的字串、列表、元組、集合與字典這五種資料類型的定義與基本操作。這些資料類型各具特點,例如字串和元組是不可變的,列表和集合是可變的,適合不同的使用場景。文章中詳細介紹如何定義進行基本的操作(如添加、刪除、訪問元素等)。
Thumbnail
在資料結構與演算法裡, 最簡單的線性資料結構除了list之外就是linked list鏈結串列了。 Linked list又有分為單向Singly linked list 和雙向Doubly linked list 在這篇文章,會從最基礎的Singly linked list開始講起。 定義
Thumbnail
在 Python 中,tuple 與 List有一個關鍵的不同點:tuple 是不可變的,這意味著一旦創建了 tuple,就無法更改其內容。 這與 List的可變性形成了對比,list 可以新增、刪除或修改元素。 元素的意思: 元素:指的是 List 中的每一個獨立的項目或值。
Thumbnail
今天要來介紹的是Python中資料型別的函數, 這幾天學習的素材是Youtube上“程式柴大大的Python 6 小時初學者課程”,一步一步帶著大家操作並解,學習中也別忘了要多多練習,練習的部分我是把我學到的東西請Chatgpt幫我出類似的題型並讓我練習。 以下我先寫出一個簡單的code,再加以
Thumbnail
Array可以說是各種語言除了基本型別之外,最常用的資料型別與容器之一了。 Array 這種連續格子狀的資料結構,在Python要怎麼表達呢? 建立一個空的陣列 最簡單也最直接的寫法就是 array = [] # Python list [] 就對應到大家熟知的array 陣列型態的資料結
Thumbnail
給定一個輸入陣列,每一個tuple代表節點之間了從屬關係。 請從從屬關係重建整顆二元樹,並且返回整顆二元樹的根結點。
Thumbnail
本文詳細介紹了Python中的各種資料型別,包括整數、字串、清單、元組、集合和字典,並提供了相關的操作範例。此外,還解釋了如何在Python中定義和操作變數,包括如何同時對多個變數進行賦值。
Thumbnail
本文介紹了串列運算式的應用,以及與Lambda匿名函式方法的比較,並提供了程式範例。串列運算式提供了一種簡潔的語法,用於創建、轉換和過濾列表。lambda函式用於創建匿名函式,通常用於簡單的操作。建議在比較複雜的情況下使用一般for迴圈加if來表示。
Thumbnail
列表(List)和元組(Tuple)都是 Python 中用來存儲集合元素的數據結構,兩者看起來很像,在初學時很容易搞混,所以觀念要建立好。 可以把列表(List)和元組(Tuple)想像成是一個容器,什麼元素都可以塞