diff --git a/_drafts/python-object-replacement.md b/_drafts/python-object-replacement.md index 191e5d9..c033425 100644 --- a/_drafts/python-object-replacement.md +++ b/_drafts/python-object-replacement.md @@ -44,7 +44,7 @@ a = [1, 2, 3, 4] ...we are creating a list object with four integers, and binding it to the name `a`. In graph form:[2] -%3L[1, 2, 3, 4]aaa->L +L[1, 2, 3, 4]aaa->L In each of the following examples, we are creating new _references_ to the list object, but we are never duplicating it. Each reference points to the same @@ -74,7 +74,7 @@ d = wrapper(a) {% endhighlight %} -%3cluster0dobj[1, 2, 3, 4]aaa->objbbb->objcc.datac->objLLL->obj +cluster0dobj[1, 2, 3, 4]aaa->objbbb->objcc.datac->objLLL->obj Note that these references are all equal. `a` is no more valid a name for the list than `b`, `c.data`, or `L` (from the perspective of `d`, which is exposed @@ -733,9 +733,9 @@ TypeError: readonly attribute Python clearly doesn't want us to re-bind bound methods, and a reasonable person would give up here, but we still have a few tricks up our sleeve. Let's examine the internal C structure of bound methods, -[`PyMethodObject`](https://github.com/python/cpython/blob/2.7/Include/classobject.h#L31). +[`PyMethodObject`](https://github.com/python/cpython/blob/2.7/Include/classobject.h#L31): -%3clusterPyMethodObjectobj<__main__.A object at 0xdeadbeef>structstruct _object* _ob_nextstruct _object* _ob_prevPy_ssize_t ob_refcntstruct _typeobject* ob_typePyObject* im_funcPyObject* im_selfPyObject* im_classPyObject* im_weakrefliststruct:f->obj +clusterPyMethodObjectobj<__main__.A object at 0xdeadbeef>structstruct _object* _ob_nextstruct _object* _ob_prevPy_ssize_t ob_refcntstruct _typeobject* ob_typePyObject* im_funcPyObject* im_selfPyObject* im_classPyObject* im_weakrefliststruct:f->obj The four gray fields of the struct come from [`PyObject_HEAD`](https://github.com/python/cpython/blob/2.7/Include/object.h#L78), @@ -790,6 +790,8 @@ Therefore, we can make our replacement like so: >>> offset = ptrs_in_struct * field_size + ctypes.sizeof(ctypes.c_ssize_t) >>> ctypes.memmove(id(f) + offset, ctypes.byref(ctypes.py_object(b)), field_size) 4470258440 +>>> f.__self__ is b +True >>> f() <__main__.B object at 0x10a8af290> @@ -797,7 +799,40 @@ Therefore, we can make our replacement like so: Excellent—it worked! -[`PyCFunctionObject`](https://github.com/python/cpython/blob/2.7/Include/methodobject.h#L81) +There's another kind of bound method, which is the built-in variety as opposed +to the user-defined variety we saw above. An example is `a.__sizeof__()`: + +{% highlight pycon %} + +>>> a, b = A(), B() +>>> f = a.__sizeof__ +>>> f + +>>> replace(a, b) +>>> f.__self__ +<__main__.A object at 0x10ab44b50> + +{% endhighlight %} + +This is stored internally as a +[`PyCFunctionObject`](https://github.com/python/cpython/blob/2.7/Include/methodobject.h#L81). +Let's take a look at its layout: + +clusterPyCFunctionObjectobj<__main__.A object at 0xdeadbeef>structstruct _object* _ob_nextstruct _object* _ob_prevPy_ssize_t ob_refcntstruct _typeobject* ob_typePyMethodDef* m_mlPyObject* m_selfPyObject* m_modulestruct:f->obj + +Fortunately, `m_self` here has the same offset as `im_self` from before, so we +can just use the same code: + +{% highlight pycon %} + +>>> ctypes.memmove(id(f) + offset, ctypes.byref(ctypes.py_object(b)), field_size) +4474703768 +>>> f.__self__ is b +True +>>> f + + +{% endhighlight %} ### Dictionary keys