I'm encountering a behavior that surprises me on dynamic calls:
I have a class that acts as an attribute for another class. When the container class resets this attribute, the methods of the attribute class are applied to the old instance if called dynamically, and to the new instance if called directly.
What must be done to ensure that dynamic calls are made to an attribute that has been reset?
class attributeClass():
def __init__(self):
self.dummy = 0
def printDummy(self):
print('id : '+hex(id(self)))
print(self.dummy)
def modifyDummy(self, newValue):
self.dummy = newValue
class ContainerClass():
def __init__(self):
self.attributeObject = attributeClass()
self.dic = {'dynamicCall': self.attributeObject.printDummy }
def resetAttribute(self):
self.attributeObject = attributeClass()
def main():
containerObject = ContainerClass() # create instance of ContainerClass
print('--A--: create instance of ContainerClass')
containerObject.attributeObject.printDummy() # call printDummy directly
containerObject.dic['dynamicCall']() # call printDummy dynamically
print('--B--: call to modifyDummy(5)')
containerObject.attributeObject.modifyDummy(5) # change the dummy value
containerObject.attributeObject.printDummy() # direct call
containerObject.dic['dynamicCall']() # dynamic call
print('--C--: call the reset function')
containerObject.resetAttribute() # reset the container's attribute "attributeClass"
containerObject.attributeObject.printDummy() # direct
containerObject.dic['dynamicCall']() # dynamic
if __name__ == '__main__':
main()
the above code has as output (addresses may vary):
--A--: create instance of ContainerClass
id : 0x22ea3ad6ba0
0
id : 0x22ea3ad6ba0
0
--B--: call to modifyDummy(5)
id : 0x22ea3ad6ba0
5
id : 0x22ea3ad6ba0
5
--C--: call the reset function
id : 0x22ea3d78b90
0
id : 0x22ea3ad6ba0
5
It's because you cached a bound method. The bound method self.attributeObject.printDummy
is an object which has a reference to the class instance self.attributeObject
. See the reference Method Objects.
You can avoid caching it in various ways. One example is to use a lambda like the following.
class ContainerClass():
def __init__(self):
self.attributeObject = attributeClass()
self.dic = {'dynamicCall': lambda: self.attributeObject.printDummy() }