为什么在 Python 中不可能实现真正的不变性?
Why true immutability is impossible in Python?
我正在阅读 attrs 的文档。它说:
Please note that true immutability is impossible in Python
我想知道这是什么原因。为什么有人不能在 Python 中拥有不可变列表,而在 C++ 中是可能的?这里的主要区别是什么?
如果你的记忆是可变的,那么真正的不变性是不可能的。
将不变性视为检查,但不能硬性保证一切都保持不变。
实际上其他语言如 C++ 将变量视为存储容器。但是 Python 将它们视为对内存地址的引用。列表可以就地修改,即在相同的内存位置。但是我们有元组,其值不能就地更新。
我不知道为什么 python 这样对待变量,但我认为这是必要的
动态类型。 真正的不变性是不可能的,可能是指动态类型功能。你可以Google了解更多。
如果这是您想要的,请告诉我。
TLDR; “真正的不变性”只有在不透水的石碑上才有可能,但对可变性的讨论适得其反,以及为什么使用它 / 很重要。以实际错误为代价技术上正确是不值得的。
这是一个糟糕的语义论证。 Python 允许在强类型语言中重新定义不同类型的变量名,这是一些混淆的来源,但需要明确的是,变量名所指的对象可以非常正确地保持不变。
以一个包含几个数字的元组为例:
>>> tup_A = (1,2,3)
无法更改元组中任何对象的值:
>>> tup_A[0] = 10
TypeError: 'tuple' object does not support item assignment
可以用其他值覆盖变量名tup_A
,但即使它与原始对象相关,它也将是完全不同的对象。例如,元组的一部分创建了一个全新的对象,而不是原始对象的视图:
>>> id(tup_A)
2887473131072
>>> tup_A = tup_A[:1]
>>> id(tup_A)
2887473037616
我相信提到的文章也可能在某种程度上提到了创建自定义不可变类型 (classes) 的可能性。这也是一个糟糕的论点,因为有很多机制可以强制执行不变性。特别是 customizing attribute access 的工具和 @property
函数可以为此发挥很大的作用。一旦使用这些方法来实现不变性,就必须故意破坏 class 来改变不打算改变的数据。这当然是可能的,因为 python 主要作为源代码分发,但理论上 python c api 也是如此。如果您重写 python,元组不必是不可变的,但这远远超出了重点,可以说这是错误的。
不变性是一种具有特定用途的工具。尽可能使用它是个好主意,这样意外的失误会产生错误消息,而不是无声的错误。如果你遇到这样的错误,你永远不应该问“我怎样才能改变这个本应不可变的值?”,而应该问“为什么这个值不应该被改变,我打算如何利用它?” “
P.S。您甚至可以在不使用 ctypes 库编辑 cpython 的情况下通过获取元组中包含的对象的实际内存位置并覆盖指针来改变元组,但这会破坏很多事情(比如垃圾收集引用计数).不要这样做。这是另一件“离题太远”的事情。
我正在阅读 attrs 的文档。它说:
Please note that true immutability is impossible in Python
我想知道这是什么原因。为什么有人不能在 Python 中拥有不可变列表,而在 C++ 中是可能的?这里的主要区别是什么?
如果你的记忆是可变的,那么真正的不变性是不可能的。
将不变性视为检查,但不能硬性保证一切都保持不变。
实际上其他语言如 C++ 将变量视为存储容器。但是 Python 将它们视为对内存地址的引用。列表可以就地修改,即在相同的内存位置。但是我们有元组,其值不能就地更新。
我不知道为什么 python 这样对待变量,但我认为这是必要的 动态类型。 真正的不变性是不可能的,可能是指动态类型功能。你可以Google了解更多。
如果这是您想要的,请告诉我。
TLDR; “真正的不变性”只有在不透水的石碑上才有可能,但对可变性的讨论适得其反,以及为什么使用它 / 很重要。以实际错误为代价技术上正确是不值得的。
这是一个糟糕的语义论证。 Python 允许在强类型语言中重新定义不同类型的变量名,这是一些混淆的来源,但需要明确的是,变量名所指的对象可以非常正确地保持不变。
以一个包含几个数字的元组为例:
>>> tup_A = (1,2,3)
无法更改元组中任何对象的值:
>>> tup_A[0] = 10
TypeError: 'tuple' object does not support item assignment
可以用其他值覆盖变量名tup_A
,但即使它与原始对象相关,它也将是完全不同的对象。例如,元组的一部分创建了一个全新的对象,而不是原始对象的视图:
>>> id(tup_A)
2887473131072
>>> tup_A = tup_A[:1]
>>> id(tup_A)
2887473037616
我相信提到的文章也可能在某种程度上提到了创建自定义不可变类型 (classes) 的可能性。这也是一个糟糕的论点,因为有很多机制可以强制执行不变性。特别是 customizing attribute access 的工具和 @property
函数可以为此发挥很大的作用。一旦使用这些方法来实现不变性,就必须故意破坏 class 来改变不打算改变的数据。这当然是可能的,因为 python 主要作为源代码分发,但理论上 python c api 也是如此。如果您重写 python,元组不必是不可变的,但这远远超出了重点,可以说这是错误的。
不变性是一种具有特定用途的工具。尽可能使用它是个好主意,这样意外的失误会产生错误消息,而不是无声的错误。如果你遇到这样的错误,你永远不应该问“我怎样才能改变这个本应不可变的值?”,而应该问“为什么这个值不应该被改变,我打算如何利用它?” “
P.S。您甚至可以在不使用 ctypes 库编辑 cpython 的情况下通过获取元组中包含的对象的实际内存位置并覆盖指针来改变元组,但这会破坏很多事情(比如垃圾收集引用计数).不要这样做。这是另一件“离题太远”的事情。