熟悉 Python 的人都知道,它从语法上支持的内置数据结构一共四种,即 tuple, list, dict, set 等, sequence comprehension 一共有三个,唯独没有 tuple comprehension, 其形式被解析为生成器表达式,而且表示单元素的 tuple 也很不一样,需要额外添加一个在新手看来十分碍眼的 逗号 : (1,)
.
但其实我们只简单地把 tuple 理解为一个 immutable 版 list 了,并没有深刻理解到它的本质。推上有人如此一针见血地指出:
#python tip: Generally, lists are for looping; tuples for structs. Lists are homogeneous; tuples heterogeneous. Lists for variable length.
— Raymond Hettinger (@raymondh) 2013年4月17日我终于发现了, tuple 既可以当数据结构,又可以当模式来匹配变量 。所以 tuple 说穿了是一个「不规则又不可变的数据结构或模式」,自然没有「推导规律」介入的余地,也难怪没有所谓的 tuple comprehension 了。
一旦洞察 tuple 的本质后,您就发现,tuple 原来大有文章:
其一,事实上,返回多个值的函数实则是在返回一个 tuple, 只不过可以省略括号而已。一旦理解透这个,就不会再写出 tp, label_tp = [], [] if len(result) == 0 else zip(*result)
这大坑了。
其二,只有一个变量的 tuple 在模式里可谓等价于变量本身,于是 不是说 (1,)
里的逗号多余,而是它本身的括号就多余,毕竟没人爱写 (a,) = (b,)
; 其实 ()
才是唯一真正特殊的 tuple; 于是纯括号就可以被名正言顺地赋予数学上的意义了,即 (a)
等于 a
, 而且其实这还带来了方便 wrap line 的额外好处:
hfm_dir_str = (
'/home/acgtyrant/Projects/HamsterForMTK/'
'Hamster_Android_SDK/src/com/mapbar/hamster')
其三,虽说没多少人喜欢 (1,)
这写法,但我们可以分拆 sequence 值成多行,且 每行后更有一个逗号! 这对强迫症患者真是终极福音:
phodopus_evaluate_command = (
command_pathname,
'-v', video_pathname,
'--proto', proto_pathname,
'--model', model_pathname,
'--mean', mean_pathname,
'-c', cascade_model_pathname,
'--lf_proto', lf_proto_pathname,
'--lf_model', lf_model_pathname,
'--lf_mean', lf_mean_pathname,
'--noshow',
)
于是我习惯分拆函数的超长形参列表多行了,且最后一行以右括号结尾;多行的 sequence 值则倒数第二行以逗号结尾,倒数一行以右括号结尾且不缩进。
其四,tuple 不光可当安全的 immutable 数据结构,而且 效率公认比 list 好 。
此外,tuple 在模式匹配上也威力十足:
其一,方便交换变量: a, b = b, a
, 毕竟等价于 (a, b) = (b, a)
模式匹配嘛。
其二,我在 坑坑重重的 Extended Iterable Unpacking 已提到过 PEP 3132 – Extended Iterable Unpacking 和 PEP 448 – Additional Unpacking Generalizations , 您会发现原来它们也不过是模式匹配嘛。此外,我们倒不能直接用星号 unpack generator, 但照样可以模式匹配 a, *_ = range(10)
, 毕竟只要后者能迭代就行了。现在看来,由于我已经洞察了 tuple 的本质,对它原本的坏印象也没有了。
其三,如果您想要可读性更好的模式匹配,可以试试 collections.namedtuple 库。
最后谢谢 @lilydjwg 和 @catsinrain 对我原推的启发,详见此推文的 timeline:
坑坑重重的 Extended Iterable Unpacking: 学 Python 久了,就渐渐地发现了它的一些瑕疵,比如园括号不像方括号和花括号一样,即表示包含一个元素的 tuple 时要加碍眼的逗号 (1,),… https://t.co/KojV9do9Bt
— 御宅暴君 (@acgtyrant) 2016年6月2日