freezeset 和 tuple 本身

CPP*_*CPP -1 python

我想将一个 freezeset 放入其自身中并将一个元组放入其自身中。

看起来很容易。通过编写一个简单的 C++ 扩展,我能够输出以下内容:

frozenset({frozenset(...)})
((...),)
Run Code Online (Sandbox Code Playgroud)

...意味着该对象在其自身内部

是否可以仅使用 python 及其标准库来执行相同的操作?

小智 5

我找到了另一种相对简单的方法来做到这一点,至少对于 a 来说tuple,使用marshal对象序列化模块。

\n

如果您并不真正关心“如何”,那么这里有一个 TL;DR 单行代码(在 Python 3.11.3 上),它返回一个包含自身的元组:

\n
__import__(\'marshal\').loads(b\'\\xa9\\x01\\x72\\x00\\x00\\x00\\x00\')\n
Run Code Online (Sandbox Code Playgroud)\n
\n

如果您确实关心“如何”,那么该方法的总体主旨是:

\n
    \n
  1. 弄清楚 atuple的序列化方式与其他容器类型(例如list.
  2. \n
  3. 观察如何marshal序列化一个自包含列表,例如[[...]]
  4. \n
  5. 修改序列化的字节字符串 ,[[...]]使其引用 a tuple,然后反序列化。
  6. \n
\n

无需再费周折...

\n

步骤1

\n

我运行了这段代码,看看如何marshal序列化一些基本的内置容器类型:

\n
import marshal\n\nL = [1, 2, 3]   # a list\nS = {1, 2, 3}   # a set\nT = (1, 2, 3)   # a tuple\n\ndef show_serial(x): print(x, \'=>\', marshal.dumps(x).hex(\' \'))\nfor x in (L, S, T): show_serial(x)\n
Run Code Online (Sandbox Code Playgroud)\n

输出:

\n
[1, 2, 3] => db 03 00 00 00 e9 01 00 00 00 e9 02 00 00 00 e9 03 00 00 00\n{1, 2, 3} => bc 03 00 00 00 e9 01 00 00 00 e9 02 00 00 00 e9 03 00 00 00\n(1, 2, 3) => a9 03 e9 01 00 00 00 e9 02 00 00 00 e9 03 00 00 00\n
Run Code Online (Sandbox Code Playgroud)\n

输出的间隔稍有不同,以便排列整齐:

\n
[1, 2, 3] => db 03 00 00 00 e9 01 00 00 00 e9 02 00 00 00 e9 03 00 00 00\n{1, 2, 3} => bc 03 00 00 00 e9 01 00 00 00 e9 02 00 00 00 e9 03 00 00 00\n(1, 2, 3) => a9 03          e9 01 00 00 00 e9 02 00 00 00 e9 03 00 00 00\n
Run Code Online (Sandbox Code Playgroud)\n
    \n
  • 除第一个字节外,thelist和 the的字节set均相同。这告诉我第一个字节表示序列化对象的类型。
  • \n
  • 接下来,thelist和 the sethave 03 00 00 00/ the tuplehas just 03。这些字节表示容器对象的长度。至于为什么tuple它的长度只需要 1 个字节......我猜这是因为短元组(例如 )(x,y)非常(r,g,b)常见并且这节省了空间。
  • \n
  • 最后,所有三个字节字符串的最后 15 个字节都是相同的。这些代表每个容器的内容int(1),即、int(2)int(3)e9表示一个(小)整数,每个 5 字节块的剩余 4 个字节是该整数的小端值。
  • \n
\n

第2步

\n

制作一个独立的列表,并将其序列化!

\n
L = []; L.append(L); show_serial(L)   # a self-containing list\n
Run Code Online (Sandbox Code Playgroud)\n

输出:

\n
[[...]] => db 01 00 00 00 72 00 00 00 00\n
Run Code Online (Sandbox Code Playgroud)\n

我能理解这个字节字符串吗?

\n
    \n
  • 输入: type(L) is list,所以第一个字节是db- \xe2\x9c\x93
  • \n
  • length : len(L) == 1,所以接下来的 4 个字节是小尾数 1,01 00 00 00- \xe2\x9c\x93
  • \n
  • content:因此,72 00 00 00 00必须是自引用容器的特殊“this object”指示符。您还可以通过其他几种方式检查这一点:
  • \n
\n
L = [1]; L.append(L); show_serial(L)   # a list containing `1` and itself\n# output: [1, [...]] => db 02 00 00 00 e9 01 00 00 00 72 00 00 00 00\n\nL = 2*[None]; L[0] = L[1] = L; show_serial(L)   # a list containing itself twice\n# output: [[...], [...]] => db 02 00 00 00 72 00 00 00 00 72 00 00 00 00\n
Run Code Online (Sandbox Code Playgroud)\n

步骤3

\n

现在我有了构建字节字符串所需的信息,它将反序列化为自包含的tuple

\n
    \n
  • 类型:我想要一个tuple,所以第一个字节是a9

    \n
  • \n
  • length:它应该包含 1 个元素(即它本身)。与 a 不同list,smalltuple只需要一个字节来序列化其长度。所以下一个字节是01

    \n
  • \n
  • content:唯一的元素是容器本身。所以接下来的 5 个字节是72 00 00 00 00.

    \n

    b = bytes.fromhex(\'a9 01 72 00 00 00 00\')\nT = marshal.loads(b)\nprint(T)

    \n
  • \n
\n

瞧\xc3\xa0!T是一个tuple包含自身的现在!

\n
((...),)\n
Run Code Online (Sandbox Code Playgroud)\n
\n

您可以使用pickleto 序列化而不是 吗marshal

\n

看起来不像——pickle可以处理list包含自身的 a,但它不知道如何处理tuple具有相同功能的 a。RecursionError当我尝试腌制之前创建的元组时,我得到了:

\n
import pickle\npickle.dumps(T)\n\nTraceback (most recent call last):\n  File "selfref.py", line 49, in <module>\n    pickle.dumps(T)\nRecursionError: maximum recursion depth exceeded while pickling an object\n
Run Code Online (Sandbox Code Playgroud)\n

我也看不到任何可以使用pickle操作码手动组合字节字符串然后反序列化的方法。当它创建 a 时listpickle有一个APPEND操作码可以使用...但是当它创建 a 时tuple,它首先将 a 的所有内容推tuple入堆栈,然后是TUPLE操作码。那么那些内容怎么可能是tuple还不存在的呢?也许有一种我没有看到的解决方法 - 如果您知道,请发表评论并让我知道!

\n

你能用 a 做同样的事情frozenset吗?

\n

不幸的是,这似乎也不起作用。A序列化与 a或 afrozenset相同,只是第一个字节代替/ of 。但是当我尝试反序列化看起来应该是正确的字节码时,会引发一个问题......listsetbedbbcmarshal.loadsValueError

\n
b = bytes.fromhex(\'be 01 00 00 00 72 00 00 00 00\')\nF = marshal.loads(b)\n\nTraceback (most recent call last):\n  File "C:/Users/ryant/OneDrive/py/self-referencing-tuple.py", line 43, in <module>\n    F = marshal.loads(b)\nValueError: bad marshal data (invalid reference)\n
Run Code Online (Sandbox Code Playgroud)\n

可能有一些我不知道为什么这似乎不起作用的原因,而对于它来说tuple它工作得很好 - 如果您知道的话请发表评论!同样,我有兴趣知道是否可以使用ctypes@SuperStormer 的答案中的方法来创建一个frozenset包含自身的方法。

\n

tuple那么其他包含自身的对象又如何呢?

\n

您可以采用此技术来创建tuple包含更复杂模式的对象 - 但还有一些细微差别需要处理。具体来说,看起来db/a9并不总是/marshal的字节码...当 a / a包含在另一个(不同的)/中时,例如,字节码通常是/ 。listtuplelisttuplelisttuple5b29

\n

我不完全确定它们出现的不同代码和环境是什么,根据文档,“格式的详细信息是故意未记录的;它可能会在 Python 版本之间发生变化(尽管很少这样做)。 ”

\n

不管它的价值如何——这里有一些我想出的一些黑客函数,它们似乎在将list<->转换tuple为嵌套序列类型(包括包含自身的序列类型)方面效果很好marshal

\n
def tupleify(iterable=(), /):\n    \'\'\' Converts nested lists to nested tuples \'\'\'\n    Lb, Tb = iter(marshal.dumps(iterable)), list()\n    for byte in Lb:\n        if byte in (0xdb, 0x5b):\n            length = list(map(next, 4*[Lb]))\n            if any(length[1:]):\n                Tb += [byte - 0x33] + length\n            else:\n                Tb += [byte - 0x32] + length[:1]\n        else:\n            Tb.append(byte)\n    return marshal.loads(bytes(Tb))\n\ndef listify(iterable=(), /):\n    \'\'\' Converts nested tuples to nested lists \'\'\'\n    Tb, Lb = iter(marshal.dumps(iterable)), list()\n    for byte in Tb:\n        if byte in (0xa9, 0x29):\n            Lb += [byte + 0x32, next(Tb), 0, 0, 0]\n        elif byte in (0xa8, 0x28):\n            Lb.append(byte + 0x33)\n        else:\n            Lb.append(byte)\n    return marshal.loads(bytes(Lb))\n
Run Code Online (Sandbox Code Playgroud)\n