I am trying to learn about bound methods in python and have implemented the code below:
class Point: def __init__(self, x,y): self.__x=x self.__y=y def draw(self): print(self.__x, self.__y) def draw2(self): print("x",self.__x, "y", self.__y) p1=Point(1,2) p2=Point(3,4) p1.draw() p2.draw() p1.draw=draw2 p1.draw(p1)
When I run this code, the following output is produced:
1 2 3 4 Traceback (most recent call last): File "main.py", line 17, in <module> p1.draw(p1) File "main.py", line 10, in draw2 print("x",self.__x, "y", self.__y) AttributeError: 'Point' object has no attribute '__x'
Why is it not possible for me to change p1.draw() after changing it so that it points at draw2?
Well, that's what you get for trying to enforce privacy in Python.
Outside the body of the class, you must refer to the attributes
_Point__y because of name mangling.
If you change the two attributes to non-mangled names (e.g.
_y) or use the names
draw2, your code won't throw an error.
In my opinion you should think thrice before using mangled names. Write proper docstrings, but don't restrict the user of your class in such an annoying manner. Using single underscore names is well understood as "don't touch this" in the community.
As you already seem to have noticed,
p1.draw behaves differently after your monkey patch because
draw2 is not a bound method of the instance
p1, so you need to pass
p1 explicitly as the argument. I suggest that you bind the instance
draw2 by leveraging the function's descriptor protocol before you reassign the name
Putting everything together, the code
class Point: def __init__(self, x,y): self._x=x self._y=y def draw(self): print(self._x, self._y) def draw2(self): print("x",self._x, "y", self._y) p1 = Point(1,2) p2 = Point(3,4) p1.draw() p2.draw() p1.draw = draw2.__get__(p1) p1.draw()
produces the output
1 2 3 4 x 1 y 2
draw2.__get__(p1) produces a callable that behaves like
draw2, but automatically passes
p1 as the first argument.