6

I understood how to replace methods at run time in Python by going through these links.[ Link1 , Link2 , & Link3].

When I replaced a "update_private_variable" method of class A, its getting replaced but its not updating the private variable.

import types

class A:
    def __init__(self):
        self.__private_variable = None
        self.public_variable = None

    def update_private_variable(self):
        self.__private_variable = "Updated in A"

    def update_public_variable(self):
        self.public_variable = "Updated in A"

    def get_private_variable(self):
        return self.__private_variable

class B:
    def __init__(self):
        self.__private_variable = None
        self.public_variable = None

    def update_private_variable(self):
        self.__private_variable = "Updated in B"

    def update_public_variable(self):
        self.public_variable = "Updated in B"

When calling the method without replacement:

a_instance  = A()
a_instance.update_private_variable()
print(a_instance.get_private_variable())
#prints "Updated in A"   

When calling the method after replacement:

a_instance  = A()
a_instance.update_private_variable =  types.MethodType(B.update_private_variable, a_instance)
a_instance.update_private_variable()
print(a_instance.get_private_variable()) 
#prints None

Whereas replacing and calling a method which updates public variable, works fine

a_instance  = A()
a_instance.update_public_variable = types.MethodType(B.update_public_variable, a_instance)
a_instance.update_public_variable()
print(a_instance.public_variable) 
#prints 'Updated in B'

Is there any other way to replace method of an instance at runtime, so that private attributes gets updated by invoking replaced method?

Community
  • 1
  • 1
  • I get an Error on `a_instance.update_private_variable()` after rebinding `update_private_variable`. `TypeError: unbound method update_private_variable() must be called with B instance as first argument (got A instance instead)` Also there is no actual public/private system. `public_variable` and `__private_variable` are both as accessible to the user as each other. – SuperBiasedMan Nov 11 '15 at 10:33
  • 3
    You are likely running into issues with *"name mangling"*, invoked by the `__leading_double_underscore` attribute name - use a `_leading_single_underscore` to indicate private-by-convention, and reserve the double for avoiding name conflicts between subclasses. – jonrsharpe Nov 11 '15 at 10:38
  • @jonrsharpe : I tried with `_leading_single_underscore`, it works properly. But as per my requirement, i can't change the _"naming convention"_ – user3262851 Nov 11 '15 at 10:47
  • Just confirmed my problem is specific to Python 2, and it worked as you said in Python3. – SuperBiasedMan Nov 11 '15 at 10:49
  • 1
    @user3262851 if your naming convention isn't compliant with [PEP-8](http://www.python.org/dev/peps/pep-0008/) and the way Python works, *you need to change the naming convention*. – jonrsharpe Nov 11 '15 at 10:50
  • @jonrsharpe: We have used __leading_double_underscore to attributes of class, only if it has to sub-classed. This is the scenario in base class. – user3262851 Nov 11 '15 at 11:03
  • But that's exactly the *worst* time to use that, because it invokes name mangling *preventing* the sub-class from shadowing the attribute. Your convention doesn't work, so change it! – jonrsharpe Nov 11 '15 at 11:05

1 Answers1

1

The idea behind name mangling is to protect base-class variables from being messed with by sub-classes; in other words, you shouldn't use it if you think sub-classes will have a good reason to modify those same variables.

Having said that, if you are already well down that path and are unable (or unwilling) to change it now, you can still get by, but it will be ugly and brittle:

class B:

    def update_private_variable(self):
        self._A__private_variable = "Updated in B"

As you can see, you have to prefix the name-mangled variable with an _ and the name of the class the variable is mangled in. Some of the consequences:

  • if you change the name of the class, you must change the name in all references to it
  • you cannot easily use the same update_private_variable method (since you have to somehow indicate the target class... I suppose you could pass the target class in to the method, but that's just more ugliness)
Ethan Furman
  • 63,992
  • 20
  • 159
  • 237