​python 的 tuple 是不是冗余设计?

640?wx_fmt=png

觉得这个问题挺好,值得好好思考。

640?wx_fmt=png

Tuple类型对于Python自身来说是非常重要的数据类型,比如说函数调用,实际上会将顺序传入的参数先组成一个tuple;多返回值也是靠返回一个tuple来实现的。因为太常用,所以需要一个更有效的数据结构来提高效率,一个不可变的tuple对象从实现上来说可以比list简单不少。再比如说code对象会记录自己的参数名称列表,free variable名称列表等等,这些如果用list,就可能被从外部修改,这样可能导致解释器崩溃;那就只能选择改成一个函数每次都返回一个新的列表,这样又很浪费。所以即使是从解释器自身实现的角度上来说引入这样一个不可变的序列类型也是很重要的。

对程序员来说如果没有什么美学上的追求的话,tuple最大的便利在于它是一个hashable的类型,而且hash算法与值直接对应,这样在Python里很容易用多个值的组合来做key生成一个dict,比如说我们网络里有20台交换机,每个交换机有24个口,那要唯一标识每个口就需要用(交换机ID,口编号),这个tuple可以做dict的key的话,编写程序起来就很方便了。

Immutable

640?wx_fmt=png

首先说说Immutable的优势:

  1. 为什么FP在多核时代重获重视?一个很重要的原因就是FP的Immutable特性。Immutable类型不存在Mutable类型的同步问题;

  2. 因为不可变,Immutable类型的内存结构设计就少了很多假设性条件,带来的直接好处就是性能优化;

  3. Python里只有Immutable类型是Hashable的,因为同样是Immutable使得Hash Table的设计来得简单;

  4. 业务上不该改变的就不允许其发生中途变化!

640?wx_fmt=png

immutable的好处实在是太多了:

性能优化,多线程安全,不需要锁,不担心被恶意修改或者不小心修改。

Tuple的使用场景

List跟Tuple使用场景上的一点主要区别

看到好多Python程序员都喜欢第一时间就用List,不管合不合适(当然有时候是需要可修改的):

[['张三', 35], ['陈八', 28]]

  

List存放的数据应该是同质数据;而Tuple呢?其存储的应该是像数据库记录这样的结构化数据——这个区别是List和Tuple使用上最直白的区别。

所以上述代码应该改为:

[('张三', 35), ('陈八', 28)]

  

Tuple是Hashable的

这可以应用在一些有趣的场景,比如把一些“记录”作为Key:

640?wx_fmt=png

Out[11]:


   
  1. [(('张三', 1995), '√ 少林长拳'),
  2. (('张三', 1999), '√ 武当太极'),
  3. (('张三', 2015), '√ 破空神掌'),
  4. (('李七', 2007), '√ 大摔碑手'),
  5. (('李七', 2017), '√ 长空剑法')]

一个武者习武履历记录的时间轴就出来了。

此外,其实Python中大量运用Tuple。好比上图代码里,在sorted中指定排序顺序的字段。然后再看看person.items(),其结构类似上面的输出,里面同样藏着Tuple结构。

比如还有print


   
  1. In [15]: print("%s is %s." % ('Foo', 'Bar'))
  2. Foo is Bar.

Tuple解构

在上面的print里,其实就是Tuple解构。

比如:


   
  1. In [28]: name, year = ('张三', 1995)
  2. In [29]: name
  3. Out[29]: '张三'
  4. In [30]: year
  5. Out[30]: 1995
  6. In [31]: for (name, year), gongfu in sorted(person.items(), key=lambda item: (item[0], item[1])):
  7. ...: print('{n} -- {y} -- {gf}'.format(n=name, y=year, gf=gongfu))
  8. ...:
  9. 张三 -- 1995 -- √ 少林长拳
  10. 张三 -- 1999 -- √ 武当太极
  11. 张三 -- 2015 -- √ 破空神掌
  12. 李七 -- 2007 -- √ 大摔碑手
  13. 李七 -- 2017 -- √ 长空剑法

Tuple解构特性对于函数返回多值是非常有意义的。

collections.namedtuple具名元组

附带提提collections.namedtuple,一个工厂函数,其在官方文档中的定义是:

factory function for creating tuple subclasses with named fields.

Returns a new tuple subclass named typename. The new subclass is used to create tuple-like objects that have fields accessible by attribute lookup as well as being indexable and iterable...


   
  1. In [57]: from collections import namedtuple
  2. ...:
  3. ...: GF = namedtuple('GongFu', 'name, gf, pai, props')
  4. ...: data = [
  5. ...: GF('张三丰', '太极拳', '武当派', ('男', 60)),
  6. ...: GF('刘小七', '八卦掌', '八卦门', ('男', 41)),
  7. ...: GF('石破天', '无影手', '无影门', ('男', 39))
  8. ...: ]
  9. ...: for item in data:
  10. ...: print(f'{item.name} -- {item.gf} -- {item.pai} -- {item.props[0]} -- {item.props[1]}.')
  11. ...:
  12. 张三丰 -- 太极拳 -- 武当派 -- 男 -- 60.
  13. 刘小七 -- 八卦掌 -- 八卦门 -- 男 -- 41.
  14. 石破天 -- 无影手 -- 无影门 -- 男 -- 39.

collections.namedtuple可以带上名称属性,在逻辑及调试上更加清晰。在作为“记录”使用时,无疑collections.namedtuple更为合适。而且collections.namedtuple的构建在内存上跟Tuple是一样的,从而也足够优化。

而且collections.namedtuple也能够解构:


   
  1. In [65]: name, gf, pai, (props0, props1) = data[2]
  2. In [66]: name, gf, pai, props0, props1
  3. Out[66]: ('石破天', '无影手', '无影门', '男', 39)

番外篇

Tuple的一个定义:

640?wx_fmt=png

Tuple其实是在大量编程语言中得以大量使用的。在一些FP语言中Tuple的基础其实是Pair,比如Idris中,("Baz", "Foo", "Bar", 39)被当成("Baz", ("Foo", ("Bar", 39)))

640?wx_fmt=gif

“扫一扫,看一看”

文章来源: blog.csdn.net,作者:敲代码的灰太狼,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/tongtongjing1765/article/details/100582083

(完)