我们能让 1 == 2 成立吗?

Kel*_*ndy 101 python cpython python-internals

Pythonint是封装实际数值的对象。我们可以修改这个值吗,例如将对象的值设置1为 2?那么这就1 == 2变成了True

Kel*_*ndy 162

我们可以。但不要在家里这样做。说真的,该1对象在很多地方都使用,我不知道这可能会破坏什么以及可能对您的计算机产生什么影响。我拒绝承担所有责任。但我发现了解这些事情很有趣。

\n

id函数为我们提供了内存地址,而该ctypes模块让我们可以处理内存:

\n
import ctypes\n\nctypes.memmove(id(1) + 24, id(2) + 24, 4)\n\nprint(1 == 2)\n\nx = 40\nprint(x + 1)\n
Run Code Online (Sandbox Code Playgroud)\n

输出:

\n
import ctypes\n\nctypes.memmove(id(1) + 24, id(2) + 24, 4)\n\nprint(1 == 2)\n\nx = 40\nprint(x + 1)\n
Run Code Online (Sandbox Code Playgroud)\n

在线尝试一下!。我在那里尝试过,因为无论如何,这些网站都必须受到保护,免受我们的黑客攻击。

\n
\n

更多解释/分析:

\n

memmove值从2对象复制到1对象中。它们的大小各为 28 个字节,但我跳过了前 24 个字节,因为那是对象的引用计数、类型地址和值大小,我们也可以查看/验证:

\n
import ctypes, struct, sys\n\nx = 1\ndata = ctypes.string_at(id(x), 28)\nref_count, type_address, number_of_digits, lowest_digit = \\\n    struct.unpack(\'qqqi\', data)\n\nprint(\'reference count: \', ref_count, sys.getrefcount(x))\nprint(\'type address:    \', type_address, id(type(x)))\nprint(\'number of digits:\', number_of_digits, -(-x.bit_length() // 30))\nprint(\'lowest digit:    \', lowest_digit, x % 2**30)\n
Run Code Online (Sandbox Code Playgroud)\n

输出(在线尝试!):

\n
True\n42\n
Run Code Online (Sandbox Code Playgroud)\n

引用计数因调用而增加getrefcount,但我不知道为什么增加了 3。无论如何,除了我们之外还有约 134 个对象引用了该1对象,而且我们可能会搞乱所有这些对象,所以...真的不知道不要在家尝试这个。

\n

“数字”指的是 CPython 如何将ints 存储为以 2 30为基数的数字。例如,x = 2 ** 3000有 101 个这样的数字。x = 123 ** 456为了更好的测试而输出:

\n
import ctypes, struct, sys\n\nx = 1\ndata = ctypes.string_at(id(x), 28)\nref_count, type_address, number_of_digits, lowest_digit = \\\n    struct.unpack(\'qqqi\', data)\n\nprint(\'reference count: \', ref_count, sys.getrefcount(x))\nprint(\'type address:    \', type_address, id(type(x)))\nprint(\'number of digits:\', number_of_digits, -(-x.bit_length() // 30))\nprint(\'lowest digit:    \', lowest_digit, x % 2**30)\n
Run Code Online (Sandbox Code Playgroud)\n

  • 对我来说,这使翻译崩溃了。尝试使用一些很少使用的值(17 和 18)确实有效! (8认同)
  • @RichardKYu 我最初使用 `1 + 1 == 42` 作为目标示例,因此将 `1` 的值更改为 21。然后,当我切换到“1 == 2”作为目标示例时,我忘记在代码中将“21”更改为“2”。 (6认同)
  • 显然这*仅*在 CPython 中有效。我很确定Python语言不会为“id”返回的值提供任何语义,这个答案说明了为什么使用内存地址可能不是最好的选择......当然它可能是最简单和最快的 (2认同)
  • @Eric Duminil 啊,“range_iterator”确实有这种优化,但为时已晚!`range` 对象使用 Python `1` 作为 `step`,然后迭代器读取它的值 2。在这里,我认为缺乏优化可能是 [`range` 如此慢的另一个原因](https ://tio.run/##RY5LDoMwDET3OYWXAbEAsUFIXKFXqPiYNlJip66R6OlTKLTdeeaNRxNfemeqmygpzcIB1AV0Ci5EFj2V@RCnKMrsn18oGLE/4cje46iO6YcnfCxoDEEHVZnnjZlZ4AqOQHq6oa2z1sDurX@Psu KstRcmLID2EERxpPYYY30fhqlvj367FlBuT7SEAaWrtnPNUnoD)。 (2认同)

Hol*_*way 39

在 Python 2 中,有一种更简单的方法 -True并且False不受保护,因此您可以将内容分配给它们。

>>> True = False
>>> (1 == 2) is True
True
Run Code Online (Sandbox Code Playgroud)

  • 如果“True = False”,那么“False is True”不应该是“False”(因为它是“True”)吗? (9认同)
  • 不过,并没有按照要求的方式去做。也许我应该保留标题中的措辞。尽管根据[Python 2对真/假的定义](https://docs.python.org/2.7/library/stdtypes.html#truth-value-testing),我认为你可以*切换*`的值True` 和 `False` 仍然认为已经达到了预期的结果(尽管仍然没有按照要求的方式)。不管怎样,我给+1,因为它至少实现了“(1 == 2) is True”,而且也很有趣。 (6认同)
  • 只是一些元想法:我的问题的起源是有人提出了另一个任务的解决方案,他们打算通过否定输入数字来“标记”输入数字来使用 O(1) 额外内存。然后我指出,在 Python 中,求反的数字是新对象,因此它们占用了 O(n) 额外的内存。这让我想知道我们是否可以“修改”给定的 int 对象来真正实现 O(1)。所以我的意思是“make 1 == 2”,如“*make 1* equal 2”,如“修改 1,使其等于 2”。但现在我很高兴我没有表达得那么好,因为我和许多其他人都很感谢你对我所表达的内容的回答。 (2认同)

dan*_*n04 11

如果您不想弄乱缓存int或对象的实际内容,您可以像这样bool伪造:1 == 2

>>> import builtins
>>> import sys
>>>
>>> def displayhook(value):
...     if value is False:
...         value = True
...     elif value is 1:
...         value = 2
...     text = repr(value)
...     sys.stdout.write(text)
...     sys.stdout.write('\n')
...     builtins._ = value
...
<stdin>:4: SyntaxWarning: "is" with a literal. Did you mean "=="?
>>> sys.displayhook = displayhook
>>> 1
2
>>> 1 == 2
True
Run Code Online (Sandbox Code Playgroud)

  • 对于像我这样不太了解 python 的人来说,知道这是一个交互式解释器会话可能很有用,因此 [`sys.displayhook()`](https://docs.python.org/ 3.8/library/sys.html#sys.displayhook) 可用于修改输出:“此函数将 repr(value) 打印到 sys.stdout”。我不知道的另一件事是:“_”指的是交互式会话中最后执行的语句的结果。 (2认同)