简体   繁体   中英

frozenset and tuple inside itself

I want to put a frozenset into itself and to put a tuple into itself.

It seems easy. By writing a simple c++ extension, I was capable to output this:

frozenset({frozenset(...)})
((...),)

... means that object is inside itself

Is it possbile to do the same using just python and it's standard library?

This is indeed possible with ctypes, Note that this code only works properly on 64-bit systems, as it assumes that ssize_t and pointers are the same size.

import ctypes

x = (0, )
(ctypes.c_void_p * 4).from_address(id(x))[3] = ctypes.cast(id(x), ctypes.c_void_p)
print(x)
# ((...),)

Explanation

typedef struct {
    PyObject_VAR_HEAD
    /* ob_item contains space for 'ob_size' elements.
       Items must normally not be NULL, except during construction when
       the tuple is not yet visible outside the function that builds it. */
    PyObject *ob_item[1];
} PyTupleObject;

( https://github.com/python/cpython/blob/main/Include/cpython/tupleobject.h )

This is the struct definition for tuple objects, straight from the CPython source. Our goal is to override the first element of ob_item with a pointer to the tuple itself.

#define PyObject_VAR_HEAD      PyVarObject ob_base;

// ...

struct _object {
    _PyObject_HEAD_EXTRA
    Py_ssize_t ob_refcnt;
    PyTypeObject *ob_type;
};

typedef struct {
    PyObject ob_base;
    Py_ssize_t ob_size; /* Number of items in variable part */
} PyVarObject;

( https://github.com/python/cpython/blob/main/Include/object.h )

We can see that a PyTupleObject will contain the following 3 fields prior to the ob_item array: ob_refcnt (a ssize_t ), ob_type (a pointer) and ob_size (another ssize_t ) ( _PyObject_HEAD_EXTRA is only used in debug builds IIRC).

We can take the lazy way out and assume that ssize_t and pointers are the same size (which works for 64-bit systems), which lets us treat the PyTupleObject as an array of void pointers.

Finally, we change the 4th pointer in this array (which will be the pointer to the first element of the tuple) to point to the address of the tuple itself (which we get via the CPython-specific implementation detail of id returning the address of an object).

A proper solution would involve creating custom ctypes.Structure classes to represent the structs instead of an system-specific array hack.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM