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
留言分享你的想法!
avatar-img
茶桁的沙龍
9會員
62內容數
从基础开始,再到Python,然后是CV、BI、NLP等相关技术。从头到尾详细的教授一边人工智能。
茶桁的沙龍的其他內容
2023/08/22
虽然是最后一节课了,但是本节课的任务却是一点也不轻松。相比较而言,如果你以后从事的是数据治理和分析工作,那么本节课的内容可能会是你在今后工作中用到的最多的内容。我们需要学习行列索引的操作,数据的处理,数据的合并,多层索引,时间序列,数据的分组聚合(重点)。最后,我们会有一个案例的展示。
Thumbnail
2023/08/22
虽然是最后一节课了,但是本节课的任务却是一点也不轻松。相比较而言,如果你以后从事的是数据治理和分析工作,那么本节课的内容可能会是你在今后工作中用到的最多的内容。我们需要学习行列索引的操作,数据的处理,数据的合并,多层索引,时间序列,数据的分组聚合(重点)。最后,我们会有一个案例的展示。
Thumbnail
2023/08/21
Hi,大家好。我是茶桁。 上一节课中,我们学习了matplotlib. 实际上,我们已经进入了数据可视化阶段。 可是在上一节课中,所有的数据都是我们固定写好的,包括两个电影的数据展示的案例(柱状图和直方图),都是我们将数据手动写成了数据列表,然后直接使用。 在我们平时的工作中,不太有那么多的机
Thumbnail
2023/08/21
Hi,大家好。我是茶桁。 上一节课中,我们学习了matplotlib. 实际上,我们已经进入了数据可视化阶段。 可是在上一节课中,所有的数据都是我们固定写好的,包括两个电影的数据展示的案例(柱状图和直方图),都是我们将数据手动写成了数据列表,然后直接使用。 在我们平时的工作中,不太有那么多的机
Thumbnail
2023/08/21
Hi, 大家好。我是茶桁。 在上一节课中,我们结束了Python正式的所有内容,但是咱们的Python课程还未结束。从这节课开始,我们要来学习一下Python的第三方库。 Python的生态非常完善也非常活跃,我们不太可能讲目前所有的第三方库全部都介绍一遍,只介绍几个有影响力并且和处理数据相关的
Thumbnail
2023/08/21
Hi, 大家好。我是茶桁。 在上一节课中,我们结束了Python正式的所有内容,但是咱们的Python课程还未结束。从这节课开始,我们要来学习一下Python的第三方库。 Python的生态非常完善也非常活跃,我们不太可能讲目前所有的第三方库全部都介绍一遍,只介绍几个有影响力并且和处理数据相关的
Thumbnail
看更多
你可能也想看
Thumbnail
TOMICA第一波推出吉伊卡哇聯名小車車的時候馬上就被搶購一空,一直很扼腕當時沒有趕緊入手。前陣子閒來無事逛蝦皮,突然發現幾家商場都又開始重新上架,價格也都回到正常水準,估計是官方又再補了一批貨,想都沒想就立刻下單! 同文也跟大家分享近期蝦皮購物紀錄、好用推薦、蝦皮分潤計畫的聯盟行銷!
Thumbnail
TOMICA第一波推出吉伊卡哇聯名小車車的時候馬上就被搶購一空,一直很扼腕當時沒有趕緊入手。前陣子閒來無事逛蝦皮,突然發現幾家商場都又開始重新上架,價格也都回到正常水準,估計是官方又再補了一批貨,想都沒想就立刻下單! 同文也跟大家分享近期蝦皮購物紀錄、好用推薦、蝦皮分潤計畫的聯盟行銷!
Thumbnail
每年4月、5月都是最多稅要繳的月份,當然大部份的人都是有機會繳到「綜合所得稅」,只是相當相當多人還不知道,原來繳給政府的稅!可以透過一些有活動的銀行信用卡或電子支付來繳,從繳費中賺一點點小確幸!就是賺個1%~2%大家也是很開心的,因為你們把沒回饋變成有回饋,就是用卡的最高境界 所得稅線上申報
Thumbnail
每年4月、5月都是最多稅要繳的月份,當然大部份的人都是有機會繳到「綜合所得稅」,只是相當相當多人還不知道,原來繳給政府的稅!可以透過一些有活動的銀行信用卡或電子支付來繳,從繳費中賺一點點小確幸!就是賺個1%~2%大家也是很開心的,因為你們把沒回饋變成有回饋,就是用卡的最高境界 所得稅線上申報
Thumbnail
在這篇Python筆記中,我們將探索如何使用元組來管理地理資訊數據。元組是一種有序且不可變的資料結構,非常適合用於儲存地理座標等固定不變的資料。通過實際的Coding Example,我們將展示如何定義元組、進行解包操作,並如何利用元組來簡化地理資訊的管理和處理。
Thumbnail
在這篇Python筆記中,我們將探索如何使用元組來管理地理資訊數據。元組是一種有序且不可變的資料結構,非常適合用於儲存地理座標等固定不變的資料。通過實際的Coding Example,我們將展示如何定義元組、進行解包操作,並如何利用元組來簡化地理資訊的管理和處理。
Thumbnail
本篇文章探討了Python中的字串、列表、元組、集合與字典這五種資料類型的定義與基本操作。這些資料類型各具特點,例如字串和元組是不可變的,列表和集合是可變的,適合不同的使用場景。文章中詳細介紹如何定義進行基本的操作(如添加、刪除、訪問元素等)。
Thumbnail
本篇文章探討了Python中的字串、列表、元組、集合與字典這五種資料類型的定義與基本操作。這些資料類型各具特點,例如字串和元組是不可變的,列表和集合是可變的,適合不同的使用場景。文章中詳細介紹如何定義進行基本的操作(如添加、刪除、訪問元素等)。
Thumbnail
在資料結構與演算法裡, 最簡單的線性資料結構除了list之外就是linked list鏈結串列了。 Linked list又有分為單向Singly linked list 和雙向Doubly linked list 在這篇文章,會從最基礎的Singly linked list開始講起。 定義
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 中,tuple 與 List有一個關鍵的不同點:tuple 是不可變的,這意味著一旦創建了 tuple,就無法更改其內容。 這與 List的可變性形成了對比,list 可以新增、刪除或修改元素。 元素的意思: 元素:指的是 List 中的每一個獨立的項目或值。
Thumbnail
今天要來介紹的是Python中資料型別的函數, 這幾天學習的素材是Youtube上“程式柴大大的Python 6 小時初學者課程”,一步一步帶著大家操作並解,學習中也別忘了要多多練習,練習的部分我是把我學到的東西請Chatgpt幫我出類似的題型並讓我練習。 以下我先寫出一個簡單的code,再加以
Thumbnail
今天要來介紹的是Python中資料型別的函數, 這幾天學習的素材是Youtube上“程式柴大大的Python 6 小時初學者課程”,一步一步帶著大家操作並解,學習中也別忘了要多多練習,練習的部分我是把我學到的東西請Chatgpt幫我出類似的題型並讓我練習。 以下我先寫出一個簡單的code,再加以
Thumbnail
Array可以說是各種語言除了基本型別之外,最常用的資料型別與容器之一了。 Array 這種連續格子狀的資料結構,在Python要怎麼表達呢? 建立一個空的陣列 最簡單也最直接的寫法就是 array = [] # Python list [] 就對應到大家熟知的array 陣列型態的資料結
Thumbnail
Array可以說是各種語言除了基本型別之外,最常用的資料型別與容器之一了。 Array 這種連續格子狀的資料結構,在Python要怎麼表達呢? 建立一個空的陣列 最簡單也最直接的寫法就是 array = [] # Python list [] 就對應到大家熟知的array 陣列型態的資料結
Thumbnail
給定一個輸入陣列,每一個tuple代表節點之間了從屬關係。 請從從屬關係重建整顆二元樹,並且返回整顆二元樹的根結點。
Thumbnail
給定一個輸入陣列,每一個tuple代表節點之間了從屬關係。 請從從屬關係重建整顆二元樹,並且返回整顆二元樹的根結點。
Thumbnail
本文詳細介紹了Python中的各種資料型別,包括整數、字串、清單、元組、集合和字典,並提供了相關的操作範例。此外,還解釋了如何在Python中定義和操作變數,包括如何同時對多個變數進行賦值。
Thumbnail
本文詳細介紹了Python中的各種資料型別,包括整數、字串、清單、元組、集合和字典,並提供了相關的操作範例。此外,還解釋了如何在Python中定義和操作變數,包括如何同時對多個變數進行賦值。
Thumbnail
本文介紹了串列運算式的應用,以及與Lambda匿名函式方法的比較,並提供了程式範例。串列運算式提供了一種簡潔的語法,用於創建、轉換和過濾列表。lambda函式用於創建匿名函式,通常用於簡單的操作。建議在比較複雜的情況下使用一般for迴圈加if來表示。
Thumbnail
本文介紹了串列運算式的應用,以及與Lambda匿名函式方法的比較,並提供了程式範例。串列運算式提供了一種簡潔的語法,用於創建、轉換和過濾列表。lambda函式用於創建匿名函式,通常用於簡單的操作。建議在比較複雜的情況下使用一般for迴圈加if來表示。
Thumbnail
列表(List)和元組(Tuple)都是 Python 中用來存儲集合元素的數據結構,兩者看起來很像,在初學時很容易搞混,所以觀念要建立好。 可以把列表(List)和元組(Tuple)想像成是一個容器,什麼元素都可以塞
Thumbnail
列表(List)和元組(Tuple)都是 Python 中用來存儲集合元素的數據結構,兩者看起來很像,在初學時很容易搞混,所以觀念要建立好。 可以把列表(List)和元組(Tuple)想像成是一個容器,什麼元素都可以塞
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News