元组
#使用()来创建元组
# 创建了一个空元组
my_tuple = ()
my_tuple = tuple()
# 虽然并非必须,元组通常用括号括起来
t = ('a', 'b', 'c', 'd', 'e')
# 当元组不是空元组时,括号可以省略
t = 'a', 'b', 'c', 'd', 'e'
# 如果元组不是空元组,它里边至少要有一个,
t = 40,
a = (11,)
a (11,)
# 将值放置在括号中并不会创建元组:
>>> t2 = ('a')
>>> type ( t2 )
<class 'str '>
# 元组内元素可以混杂其他类型
a = ('a', 1.5, True, [2, 3, 4]) # 各种类型混杂
另一个建立元组的方法是使用内建函数 tuple。
# 在没有参数传递时它会产生一个空元组。
>>> t = tuple ()
>>> t
()
# 如果实参是一个序列 (字符串、列表或者元组),结果将是包含序列内元素的一个元组。
>>> t = tuple ('lupins ')
>>> t
('l', 'u', 'p', 'i', 'n', 's')
- 可将元组利用推荐式生成列表,也可用以下方式生成元组:
t = tuple((i for i in range(3)))
# t: (0, 1, 2)
元组的解包(解构)
- 交互a 和 b的值,这时我们就可以利用元组的解包
a = 100
b = 300
# print(a , b)
# 交互a 和 b的值,这时我们就可以利用元组的解包
a , b = b , a
- 解包指就是将元组当中每一个元素都赋值给一个变量
my_tuple = 10 , 20 , 30 , 40
a,b,c,d = my_tuple
- 在对一个元组进行解包时,变量的数量必须和元组中的元素的数量一致
- 也可以在变量前边添加一个*,这样变量将会获取元组中所有剩余的元素
a , b , *c = my_tuple
a , *b , c = my_tuple
*a , b , c = my_tuple
a , b , *c = [1,2,3,4,5,6,7]
a , b , *c = 'hello world'
# 不能同时出现两个或以上的*变量
# *a , *b , c = my_tuple SyntaxError: two starred expressions in assignment
元组相当于一个不可变的列表
- 元组是一个不可变的序列
- 它的操作的方式基本上和列表是一致的. 除了跟增减元素相关的方法之外,元组支持列表的其他所有方法。
- 所以你在操作元组时,就把元组当成是一个不可变的列表就ok了
- 一般当我们希望数据不改变时,就使用元组,其余情况都使用列表
元组是不可修改的,我们修改元素时,就会报错。但是,我们可以修改混杂类型里的列表类型数据。
a = ('a', 1.5, True, [2, 3, 4])]
a[-1].append(5)
# ('a', 1.5, True, [2, 3, 4, 5])
del a # 删除整个元组
具名元组
在访问元组数据的时候我们发现,要取指定的数据需要知识它所在的索引位置,不是很方便。命名元组可以解决这个问题
- collections.namedtuple是一个工厂函数,它可以用来构建一个带字段名的元组和一个有名字的类----这个带名字的类对调试程序有很大帮助。
- 除了从普通元组那里继承来的属性之外,具名元组还有一些自己专有的属性。比如: _fields 类属性、类方法 _make(iterable) 和实例方法 _asdict():
- _fields 属性是一个包含这个类所有字段名称的元组
- 用 _make() 通 过 接 受 一 个 可 迭 代 对 象 来 生 成 这 个 类 的 一 个 实 例, 它 的 作 用 跟City(*delhi_data) 是一样的。
- _asdict() 把具名元组以 collections.OrderedDict 的形式返回,我们可以利用它来把元组里的信息友好地呈现出来。
对于通过名称访问相比通过索引访问更清晰的异构数据多项集,collections.namedtuple() 可能是比简单元组对象更为合适的选择。
demo:
from collections import namedtuple
Girl = namedtuple('Girl', ['name', 'age'])
lily = Girl('lily', 10)
lily.name # lily
lily.age # 10
lily
# Girl(name='lily', age=10)
type(lily)
# __main__.Girl
# 不能被修改
lily.age = 12
# AttributeError: "can't set attribute"
demo:
from collections import namedtuple
# 构建一个类表示一一个城市
# 第一个参数:一个是类名
# 第二个参数:一个是类的各个字段的名字
# # 可以是由数个字符串组成的可迭代对象
# # 可以是由空格分隔开的字段名组成的字符串
City = namedtuple('City', 'name country population coordinates')
# 实例化一个对象
token = City('hangzhu', 'china', 36.2, (36, 256))
print(City._fields) #输出: ('name', 'country', 'population', 'coordinates')
LatLong = namedtuple('LatLong', 'lat long')
delhi_data = ('Delhi NCR', 'IN', 21.935, LatLong(28.613889, 77.208889))
delhi = City._make(delhi_data)
print(delhi._asdict()) #输出: {'name': 'Delhi NCR', 'country': 'IN', 'population': 21.935, 'coordinates': LatLong(lat=28.613889, long=77.208889)}
for key, value in delhi._asdict().items():
print(key + ':', value)
"""
name: Delhi NCR
country: IN
population: 21.935
coordinates: LatLong(lat=28.613889, long=77.208889)
"""
可变长度参数元组
函数可以接受可变数量的参数。以 * 开头的形参将输入的参数 汇集到一个元组中。例如,printall 可以接受任意数量的参数,并将它们打印出来:
def printall (* args ) :
print ( args )
汇集的形参可以使用任意名字,但是习惯使用 args。以下是这个函数的调用效果:
>>> printall (1 , 2.0 , '3')
(1 , 2.0 , '3')
与汇集相对的是分散 scatter。如果你有一个值的序列,并且希望将其作为多个参数传递给一个函数,你可以使用运算符 *。
>>> t = (7 , 3)
>>> divmod (* t )
(2 , 1)
元组作为返回值
严格地说,一个函数只能返回一个值,但是如果这个返回值是元组,其效果等同于返回多个值。
def min_max ( t ) :
return min (t ) , max ( t )
还可以用*
处理剩下的元素:
>>> a, b, *rest = range(5)
>>> a, b, rest
(0, 1, [2, 3, 4])
>>> a, b, *rest = range(3)
>>> a, b, rest
(0, 1, [2])
>>> a, b, *rest = range(2)
>>> a, b, rest
(0, 1, [])
在平行赋值中,* 前缀只能用在一个变量名前面,但是这个变量可以出现在赋值表达式的任意位置:
>>> a, *body, c, d = range(5)
>>> a, body, c, d
(0, [1, 2], 3, 4)
>>> *head, b, c, d = range(5)
>>> head, b, c, d
([0, 1], 2, 3, 4)
列表和元组
zip 是一个内建函数,可以接受将两个或多个序列组,并返回一个元组列表,其中每个元组包含了各个序列中相对位置的一个元素。这个函数的名称来自名词拉链 (zipper),后者将两片链齿连接拼合在一起。
>>> s = 'abc '
>>> t = [0 , 1 , 2]
>>> zip (s , t ) #输出的结果是一个zip 对象
<zip object at 0 x7f7d0a9e7c48 >
# 遍历zip对象
# zip对象是一个友善的迭代器,是指任何一种能够按照某个序列迭代的对象
# 迭代器在某些方面与列表非常相似,不同之处在于,你无法通过索引来选择迭代器中的某个元素。
>>> for pair in zip (s , t ) :
... print ( pair )
...
('a', 0)
('b', 1)
('c', 2)
如果你想使用列表操作符和方法,你可以通过 zip对象创造一个列表:
>>> list ( zip (s , t ) )
[( 'a', 0) , ('b', 1) , ('c', 2) ]
结果就是一个包含若干元组的列表;在这个例子中,每个元组又包含了字符串中的一个字符和列表 t 中对应的一个元素。
如果用于创建的序列长度不一,返回的对象的长度以最短序列的长度为准。
>>> list ( zip ('Anne ', 'Elk ') )
[( 'A', 'E') , ('n', 'l') , ('n', 'k')]
你可以在 for 循环中使用元组赋值,遍历包含元组的列表:
t = [( 'a', 0) , ('b', 1) , ('c', 2) ]
for letter , number in t :
print ( number , letter )
输出:
0 a
1 b
2 c
如果将 zip、for循环和元组赋值结合起来使用,你会得到一个可以同时遍历两个 (甚至多个) 序列的惯用法。
def has_match ( t1 , t2 ) :
for x , y in zip (t1 , t2 ) :
if x == y :
return True
return False
如果需要遍历一个序列的元素以及它们的索引号,你可以使用内建函数 enumerate:
for index , element in enumerate ('abc '):
print ( index , element )
输出:
0 a
1 b
2 c
这里有一个非常值得探讨的问题,我们已经有了列表这种数据结构,为什么还需要元组这样的类型呢?
- 元组中的元素是无法修改的,事实上我们在项目中尤其是多线程中可能更喜欢使用的是那些不变对象(一方面因为对象状态不能修改,所以可以避免由此引起的不必要的程序错误,简单的说就是一个不变的对象要比可变的对象更加容易维护;另一方面因为没有任何一个线程能够修改不变对象的内部状态,一个不变对象自动就是线程安全的,这样就可以省掉处理同步化的开销。一个不变对象可以方便的被共享访问)。所以结论就是:如果不需要对元素进行添加、删除、修改的时候,可以考虑使用元组,当然如果一个方法要返回多个值,使用元组也是不错的选择。
- 元组在创建时间和占用的空间上面都优于列表。
# 将列表转换成元组
fruits_list = ['apple', 'banana', 'orange']
fruits_tuple = tuple(fruits_list)
print(fruits_tuple)
t = ('王大锤', 20, True, '云南昆明')
print(t)
# 将元组转换成列表
person = list(t)
print(person)
字典和元组
字典对象有一个内建方法叫做 itmes ,它返回由多个元组组成的序列,其中每个元组是一个键值对。
>>> d = {'a':0 , 'b':1 , 'c':2}
>>> t = d.items ()
>>> t
dict_items ([( 'c', 2) , ('a', 0) , ('b', 1) ])
其结果是一个 dict_itmes 对象,这是一个对键值对进行迭代的迭代器。你可以在 for 循环中像这样使用它:
>>> for key , value in d . items () :
... print ( key , value )
...
c 2
a 0
b 1
由于是字典生成的对象,这些项是无序的。
另一方面,你可以使用元组的列表初始化一个新的字典:
>>> t = [( 'a', 0) , ('c', 2) , ('b', 1) ]
>>> d = dict ( t )
>>> d
{'a': 0 , 'c': 2 , 'b': 1}
将 dict 和 zip结合使用,可以很简洁地创建一个字典:
>>> d = dict (zip ('abc ', range (3) ) )
>>> d
{'a': 0 , 'c': 2 , 'b': 1}
字典的 update 方法也接受元组的列表,并作为键-值对把它们添加到已有的字典中。
在字典中使用元组作为键 (主要因为无法使用列表) 的做法很常见。例如,一个电话簿可能会基于用户的姓-名对,来映射至号码。假设我们已经定义了 last 、first 和 number三个变量,我们可以这样实现映射:
directory [ last , first ] = number
for last , first in directory :
print ( first , last , directory [ last , first ])
元组的CURD
python中不允许修改元组的数据,包括不能删除其中的元素。
a = (1, 2, 3)
len(a) # 3 元素个数
max(a) # 3 最大值
min(a) # 1 最小值
sum(a) # 6 求和
a.count(1) # 1 求元素的个数
a.index(2) # 1 指定元素位置
for i in a: print(i) # 迭代元素
sorted(a) # 返回一个排序的列表,但不改变原列表
any(a) # True 是否至少有一个元素为真
all(a) # True 是否所有元素为真
tuple.__dir__('') # 查看元组支持的所有函数和方法
元组的不可变性
元组与多数 Python 集合(列表、字典、集,等等)一样,保存的是对象的引用。
- 如果引用的元素是可变的,即便元组本身不可变,元素依然可变。
- 元组的不可变性其实是指 tuple 数据结构的物理内容(即保存的引用)不可变,与引用的对象无关。
元组的值会随着引用的可变对象的变化而变。元组中不可变的是元素的标识。
t1 = (1, 2, [30, 40])
t2 = (1, 2, [30, 40])
print(t1 == t2) # true:虽然 t1 和 t2 是不同的对象,但是二者相等
print(t1[-1]) #[30, 40]
t1[-1].append(99) #就地修改 t1[-1] 列表。
print(t1) #(1, 2, [30, 40, 99])
print(t1[-1]) #[30, 40, 99]
print(t1 == t2) # False
序列嵌套
在很多情况下,不同类型的序列 (字符串、列表、元组) 可以互换使用。因此,我们如何选用合适的嵌套对象呢?
首先,显而易见的是,字符串比其他序列的限制更多,因为它的所有元素都是字符,且字符串不可变。如果你希望能够改变字符在字符串中的位置,使用列表嵌套字符比较合适。
列表比元组更常见,这源于它们可变性的易用。但是有些情况下,你会更倾向于使用元组:
- 在一些情况下 (例如 return语句),从句式上生成一个元组比列表要简单。
- 如果你想使用一个序列作为字典的键,那么你必须使用元组或字符串这样的不可变类型。
- 如果你向函数传入一个序列作为参数,那么使用元组以降低由于别名而产生的意外行为的可能性。
正由于元组的不可变性,它们没有类似 sort 和 reverser 这样修改现有列表的方法。然而 Python 提供了内建函数 sorted,用于对任意序列排序并输出相同元素的列表,以及reversed,用于对序列逆向排序并生成一个可以遍历的迭代器。