在Python函数中,是否有可能强制实现数据不可变性?

3

假设我定义了一个列表对象,并且只希望在我定义的函数中以只读方式访问。在C++中,你可以使用常量引用实现这一点。在Python函数中是否有实现这种功能的方法?从编写"pythonic"代码的角度来看,这样做是否有意义,或者我只是试图错误地应用C++范例?


2
使用元组而不是列表。 - Barmar
1
在Python中,可变性是类型的一个特征,而不是特定实例或变量的特征。 - Barmar
3个回答

2
使用元组tuple代替:
>>> hash([1,2,3])
Traceback (most recent call last):
  File "<pyshell#7>", line 1, in <module>
    hash([1,2,3])
TypeError: unhashable type: 'list'
>>> hash((1,2,3))
2528502973977326415
>>> 

哈希可以定义其是否可变,并在它是可变的情况下产生TypeError: unhashable type错误,例如list。相反,tuple会返回一些较大的数字(在这种特定情况下不需要关心),如果您想要,我们可以使用tryexcept

“原始回答”翻译成中文为“最初的回答”。

try:
    hash((1,2,3))
    print('Immutable object')
except:
    print('Mutable object')

最初的回答是:

输出结果为:

Immutable object

1
在Python中,所有变量都是引用,所以有一个C的相当于const也不会起作用。如果指向的对象是可变的,你可以改变它;如果是不可变的,则无法更改。
然而,有些类型是可变的,有些类型则不是。列表是可变的,但元组则不是。因此,如果将数据作为元组传递,就保证了函数无法对其进行任何操作。
用C的比喻来说,整数列表有点像int [],而整数元组则是const int []。当你将它们传递给函数时,无论你传递的是const引用还是非const引用都没有关系;重要的是它所引用的东西是否是const

0

使用元组是最常见的方法,如其他答案中所解释的那样。假设您从一个列表实例开始,其他可能的策略包括:

  • 如果函数中列表的作用只是循环遍历,请传递迭代器而不是列表本身- iterator = iter(my_list)。请注意,您只能对迭代器进行一次循环;如果需要多次循环,则可以传递重复生成iter(my_list)的生成器。
  • 传递列表的副本而不是列表本身:new_list = my_list.copy()new_list = my_list[:]。由于内存消耗,这可能不是非常大的列表的好策略。

无论您选择哪种方法-甚至是元组-请注意,这些方法都假定集合中的元素本身是不可变的,例如整数和字符串。如果元素是可变的,则可以对其进行突变(但不能替换):

>>> t = ([1], [1,2])
>>> t[0] = 6
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> t[0][0] = 6
>>> t
([6], [1, 2])


>>> L = [[1], [1, 2]]
>>> for x in iter(L):
...     x[0] += 1
... 
>>> L
[[2], [2, 2]]

>>> L = [[1], [1, 2]]
>>> copy = L.copy()
>>> for x in copy:
...     x[0] += 1
... 
>>> L
[[2], [2, 2]]
>>> 

>>> copy[0] = 42
>>> copy
[42, [2, 2]]
>>> L
[[2], [2, 2]]

你可以通过使用 copy.deepcopy 来绕过这个问题,但这将会占用更多的内存。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接