为什么我们需要Python中的元组(或任何不可变的数据类型)?

为什么我们需要Python中的元组(或任何不可变的数据类型)?,python,tuples,Python,Tuples,我已经阅读了一些python教程(比如深入python),以及python.org上的语言参考——我不明白为什么该语言需要元组 元组与列表或集合相比没有方法,如果我必须将元组转换为集合或列表才能对它们进行排序,那么首先使用元组有什么意义 不变性 为什么有人关心变量在内存中的位置是否与最初分配时不同?Python中的不变性似乎被过分强调了 在C/C++中,如果我分配一个指针并指向某个有效内存,我不在乎地址在哪里,只要在使用它之前地址不为空 每当我引用那个变量时,我不需要知道指针是否仍然指向原始地址

我已经阅读了一些python教程(比如深入python),以及python.org上的语言参考——我不明白为什么该语言需要元组

元组与列表或集合相比没有方法,如果我必须将元组转换为集合或列表才能对它们进行排序,那么首先使用元组有什么意义

不变性

为什么有人关心变量在内存中的位置是否与最初分配时不同?Python中的不变性似乎被过分强调了

在C/C++中,如果我分配一个指针并指向某个有效内存,我不在乎地址在哪里,只要在使用它之前地址不为空

每当我引用那个变量时,我不需要知道指针是否仍然指向原始地址。我只是检查null并使用它(或者不使用)

在Python中,当我分配一个字符串(或元组)并将其分配给x,然后修改该字符串时,为什么我要关心它是否是原始对象?只要变量指向我的数据,这就是最重要的

>>> x='hello'
>>> id(x)
1234567
>>> x='good bye'
>>> id(x)
5432167

x
仍然引用我想要的数据,为什么有人需要关心它的id是相同的还是不同的?

有时我们喜欢使用对象作为字典键

值得一提的是,最近的元组(2.6+)增长了
index()
count()
方法

,您可以看到关于这方面的一些讨论

  • 不变的对象可以实现实质性的优化;这大概就是为什么字符串在Java中也是不可变的,Java是单独开发的,但与Python几乎是同时开发的,而在真正的函数式语言中,几乎所有东西都是不可变的

  • 特别是在Python中,只有不可变的表可以散列(因此,集合的成员或字典中的键)。同样,这提供了优化,但远不止是“实质性的”(设计存储完全可变对象的像样的哈希表是一场噩梦——要么在散列对象时立即复制所有内容,要么检查对象的散列自上次引用对象后是否发生了更改,这是一场噩梦)

  • 优化问题示例:

    $ python -mtimeit '["fee", "fie", "fo", "fum"]'
    1000000 loops, best of 3: 0.432 usec per loop
    $ python -mtimeit '("fee", "fie", "fo", "fum")'
    10000000 loops, best of 3: 0.0563 usec per loop
    
    如果我必须将元组转换为集合或列表才能对它们进行排序,那么首先使用元组有什么意义

    在这种特殊情况下,可能没有任何意义。这不是一个问题,因为这不是您考虑使用元组的情况之一。

    正如您所指出的,元组是不可变的。具有不可变类型的原因适用于元组:

    • 复制效率:与复制不可变对象不同,您可以将其别名(将变量绑定到引用)
    • 比较效率:使用引用复制时,可以通过比较位置而不是内容来比较两个变量
    • 实习:你最多需要存储一个不可变值的副本
    • 不需要在并发代码中同步对不可变对象的访问
    • 常量正确性:某些值不允许更改。这(对我来说)是不可变类型的主要原因
    请注意,特定的Python实现可能不会使用上述所有特性

    字典键必须是不可变的,否则更改键对象的属性会使基础数据结构的不变量无效。因此,元组可以潜在地用作键。这是常量正确性的结果


    另请参见“,”。

    上面的答案都没有指出元组与列表的真正问题,而许多Python新手似乎并不完全理解元组与列表的关系

    元组和列表有不同的用途。列表存储同质数据。您可以而且应该有如下列表:

    ["Bob", "Joe", "John", "Sam"]
    
    ["Billy", "Bob", "Joe", 42]
    
    正确使用列表的原因是因为这些都是同质类型的数据,特别是人名。但请列举如下:

    ["Bob", "Joe", "John", "Sam"]
    
    ["Billy", "Bob", "Joe", 42]
    
    这份名单是一个人的全名和年龄。这不是一种类型的数据。存储该信息的正确方法是在元组或对象中。假设我们有几个:

    [("Billy", "Bob", "Joe", 42), ("Robert", "", "Smith", 31)]
    
    元组和列表的不变性和易变性并不是主要区别。列表是相同种类的项目的列表:文件、名称、对象。元组是一组不同类型的对象。它们有不同的用途,许多Python编码人员滥用列表来表示元组的用途

    请不要


    编辑:

    我认为这篇博文解释了为什么我认为这比我做的更好:


    它们非常重要,因为它们可以保证调用方所传递的对象不会发生变异。 如果您这样做:

    a = [1,1,1]
    doWork(a)
    
    呼叫方无法保证呼叫后a的值。 但是,

    a = (1,1,1)
    doWorK(a)
    
    现在,作为调用方或代码的读者,您知道a是相同的。
    在这种情况下,您可以复制列表并传递它,但现在您正在浪费周期,而不是使用更具语义意义的语言结构。

    我一直发现,对于相同的基本数据结构(数组),有两种完全不同的类型是一种笨拙的设计,但在实践中并不是一个真正的问题。(每种语言都有它的缺点,包括Python,但这并不是一个重要的缺点。)

    为什么有人关心变量在内存中的位置是否与最初分配时不同?Python中的不变性似乎被过分强调了

    这些是不同的事情。易变性与它存储在内存中的位置无关;这意味着它指向的东西不能改变

    Python对象在创建后不能更改位置,无论是否可变。(更准确地说,id()的值不能更改——实际上也是一样。)可变对象的内部存储可以更改,但这是一个隐藏的实现细节

    >>> x='hello'
    >>> id(x)
    1234567
    >>> x='good bye'
    >>> id(x)
    5432167
    
    这不是修改(“变异”)变量;是c
    >>> a1 = (1,)
    >>> a2 = a1
    >>> print a2[0]
    1
    >>> a1 = (2,)
    >>> print a2[0]
    1
    
    >>> t1 = (1,2)
    >>> d1 = { t1 : 'three' }
    >>> print d1
    {(1,2): 'three'}
    >>> t1[0] = 0  ## results in a TypeError, as tuples cannot be modified
    >>> t1 = (2,3) ## creates a new tuple, does not modify the old one
    >>> print d1   ## as seen here, the dict is still intact
    {(1,2): 'three'}
    
    blue= 0, 0, 255
    alist= ["red", "green", blue]