Java 程序猿学 python:python 的 list 和 tuple

内容纲要

问题

tuple 就是不可变的 list,这样的理解对吗?

回答

如果以 Java 程序猿来说,基本没有问题,但是在 python 的世界,并不是这么简单,tuple 相对于 list 来说,有 3 个主要的差异

  • 不可变
  • 固定长度
  • 元素的数据类型不需要保持一致

不可变

以下的代码将一个 list 转换为 tuple

>>> x = [1,2,3]
>>> y = tuple(x)
>>> y
(1, 2, 3)

将一个 list 转换为 tuple,有很多优点

  • 即使存储相同数量的元素,tuple 占用的内存要少于 list
  • tuple 不可变,这样就不存在线程安全问题,也可以作为 dict 的 key 来使用

为什么要这样转换呢?一般来说,主要是为了性能,不可变的 tuple 性能比 list 好,并且不存在线程安全问题

固定长度

这个特性很容易被忽视

所谓固定长度,意味着 tuple 的使用者对其含有几个元素是很清楚的,在上面将 list 转为 tuple 的过程中,其实我们并不知道返回的 tuple 的长度

比较典型的使用 tuple 的场景是函数返回多个值的情况,如下例

import datetime

def today():
    now = datetime.datetime.now()
    return now.year, now.month, now.day

if __name__ == '__main__':
    print(today())

调用 today() 方法返回当前的 年、月、日,如下

(2021, 1, 25)

很明显,我们知道该方法要返回 3 个值

数据类型

通常我们使用 list 时,list 里的元素是相同类型的;而使用 tuple 时,一般没有这样的共识

举个例子,当我们需要给方法传递一些参数时,在 Java 的世界里,我们可能会用 Map

比如下面的例子,将请求参数拼接到 url 里得到一个完整的 url(示例代码,未考虑 url encode)

public String getFullUrl(String url, Map<String, ?> params) {
    StringBuilder fullUrl = new StringBuilder(url);
    for (Map.Entry<String, ?> entry : params.entrySet()) {
        fullUrl.append(entry.getKey()).append('=').append(entry.getValue()).append('&');
    }
    return fullUrl.toString();
}

又比如 mybatis 用 Map 传递多个参数

思考下,这样的场景真的需要用 map 吗?从代码里可以看到,我们真正需要的是一个 key-value 的列表,即 params.entrySet()

如果有 List<KeyValuePair> 这样的数据结构,完全可以应对这样的使用场景,且资源的消耗要少于 Map

在 python 的世界里,我建议用 tuple 列表,即

[(k1, v1), (k2, v2), (k3, v3)...]

注意: key 和 value 的数据类型是可以不一样的

大家可以留意下,python 的很多函数是既支持 dict 参数,也支持 tuple 列表参数的,比如 request 库,看下其最核心方法 request 的文档里关于以下参数的说明

  • params – (optional) Dictionary, list of tuples or bytes to send in the query string for the Request.
  • data – (optional) Dictionary, list of tuples, bytes, or file-like object to send in the body of the Request.

结论

tuple 不止是不可变的 list 这么简单,跳出 Java 程序猿思维惯性才能更好的理解 tuple

Java 程序猿学 python:python 的 list 和 tuple

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

Scroll to top
粤ICP备2020114259号 粤公网安备44030402004258