Personal website https://benkurtovic.com/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

python-object-replacement.md 4.7 KiB

9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. ---
  2. layout: post
  3. title: Replacing Objects in Python
  4. tags: Python
  5. description: More reflection than you cared to ask for
  6. draft: true
  7. ---
  8. Today, we're going to demonstrate a fairly evil thing in Python, which I call
  9. _object replacement_.
  10. Say you have some program that's been running for a while, and a particular
  11. object has made its way throughout your code. It lives inside lists, class
  12. attributes, maybe even inside some closures. You want to completely replace
  13. this object with another one; that is to say, you want to find all references
  14. to object `A` and replace them with object `B`, enabling `A` to be garbage
  15. collected.
  16. _But why on Earth would you want to do that?_ you ask. I'll focus on a concrete
  17. use case in a future post, but for now, I imagine this could be useful in some
  18. kind of advanted unit testing situation with mock objects. Still, it's fairly
  19. insane, so let's leave it as primarily an intellectual exercise.
  20. ## Review
  21. First, a recap on terminology here. You can skip this section if you know
  22. Python well.
  23. In Python, _names_ are what most languages call "variables". They reference
  24. _objects_. So when we do:
  25. {% highlight python %}
  26. a = [1, 2, 3, 4]
  27. {% endhighlight %}
  28. We are creating a list object with four integers, and binding it to the name
  29. `a`:
  30. <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="303px" height="53px" version="1.1"><defs/><g transform="translate(0.5,0.5)"><rect x="181" y="6" width="120" height="40" fill="#ffffff" stroke="#000000" pointer-events="none"/><g transform="translate(185,17)"><switch><foreignObject pointer-events="all" width="112" height="20" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 14px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 112px; white-space: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;"><font face="Courier New">[1, 2, 3, 4]</font></div></div></foreignObject><text x="56" y="17" fill="#000000" text-anchor="middle" font-size="14px" font-family="Helvetica">[Not supported by viewer]</text></switch></g><ellipse cx="26" cy="26" rx="25" ry="25" fill="#ffffff" stroke="#000000" pointer-events="none"/><g transform="translate(13,11)"><switch><foreignObject pointer-events="all" width="26" height="32" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; font-size: 24px; font-family: 'Courier New'; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 26px; white-space: normal; text-align: center;"><div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;">a</div></div></foreignObject><text x="13" y="28" fill="#000000" text-anchor="middle" font-size="24px" font-family="Courier New">[Not supported by viewer]</text></switch></g><path d="M 51 26 L 175 26" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 180 26 L 173 30 L 175 26 L 173 23 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/></g></svg>
  31. In each of the following examples, we are creating new _references_ to the
  32. list object, but we are never duplicating it. Each reference points to the same
  33. memory address (which you can get using `id(a)`, but that's a CPython
  34. implementation detail).
  35. {% highlight python %}
  36. b = a
  37. {% endhighlight %}
  38. {% highlight python %}
  39. c = SomeContainerClass()
  40. c.data = a
  41. {% endhighlight %}
  42. {% highlight python %}
  43. def wrapper(L):
  44. def inner():
  45. return L.pop()
  46. return inner
  47. d = wrapper(a)
  48. {% endhighlight %}
  49. [insert charts here]
  50. Note that these references are all equal. `a` is no more valid a name for the
  51. list than `b`, `c.data`, or `L` (from the perspective of `d`, which is exposed
  52. to everyone else as `d.func_closure[0].cell_contents`, but that's cumbersome
  53. and you would never do that in practice). As a result, if you delete one of
  54. these references—explicitly with `del a`, or implicitly if a name goes out of
  55. scope—then the other references are still around, and object continues to
  56. exist. If all of an object's references disappear, then Python's garbage
  57. collector should eliminate it.
  58. ## Fishing for references with Guppy
  59. Guppy!
  60. ## Handling different references
  61. ### Dictionaries
  62. dicts, class attributes via `__dict__`, locals()
  63. ### Lists
  64. simple replacement
  65. ### Tuples
  66. recursively replace parent since immutable
  67. ### Bound methods
  68. note that built-in methods and regular methods have different underlying C
  69. structs, but have the same offsets for their self field
  70. ### Closure cells
  71. function closures
  72. ### Frames
  73. ...