Search code examples
pythontensorflowclassobject

TensorFlow seems to modify both class and instance object


I have observed that the TensorFlow methods like assign_add and assign_sub modify the variables of both object and class (if exist). Here is a simple code to reproduce my observation. Can anyone please clarify about this behavior (assign_sub and assign_add modifying both class and instance attributes)?

#a python class
class myc_base():
    a=1.
    def __init__(self, b=1.):
        self.b=b
    def add(self, to_add=1.):
        self.a+=to_add
        self.b+=to_add
    def sub(self, to_sub=1.):
        self.a-=to_sub
        self.b-=to_sub

obj_base=myc_base()

print(f'Init.     -- class.a: {myc_base.a} | obj.a: {obj_base.a}, obj.b: {obj_base.b}')
obj_base.add(5.)
print(f'after add -- class.a: {myc_base.a} | obj.a: {obj_base.a}, obj.b: {obj_base.b}')
obj_base.sub(2.)
print(f'after sub -- class.a: {myc_base.a} | obj.a: {obj_base.a}, obj.b: {obj_base.b}')

Output:

Init.     -- class.a: 1.0 | obj.a: 1.0, obj.b: 1.0
after add -- class.a: 1.0 | obj.a: 6.0, obj.b: 6.0
after sub -- class.a: 1.0 | obj.a: 4.0, obj.b: 4.0

With TensorFlow:

import tensorflow as tf

#a class for tf operations
class myc_tf():
    a=tf.Variable(1.)
    def __init__(self, b=tf.Variable(1.)):
        self.b=b
    def add(self, to_add=1.):
        self.a.assign_add(to_add)
        self.b.assign_add(to_add)
    def sub(self, to_sub=1.):
        self.a.assign_sub(to_sub)
        self.b.assign_sub(to_sub)

obj_tf=myc_tf()

print(f'Init.     -- class.a: {myc_tf.a.numpy()} | obj.a: {obj_tf.a.numpy()}, obj.b: {obj_tf.b.numpy()}')
obj_tf.add(5.)
print(f'after add -- class.a: {myc_tf.a.numpy()} | obj.a: {obj_tf.a.numpy()}, obj.b: {obj_tf.b.numpy()}')
obj_tf.sub(2.)
print(f'after sub -- class.a: {myc_tf.a.numpy()} | obj.a: {obj_tf.a.numpy()}, obj.b: {obj_tf.b.numpy()}')

Output:

Init.     -- class.a: 1.0 | obj.a: 1.0, obj.b: 1.0
after add -- class.a: 6.0 | obj.a: 6.0, obj.b: 6.0
after sub -- class.a: 4.0 | obj.a: 4.0, obj.b: 4.0

Solution

  • a is a class attribute. b is an instance attribute.

    However, augmented assignments like

    self.a += to_add
    self.a -= to_sub
    

    are not modifying the class attribute you think you are accessing via the instance. They are really equivalent to

    self.a = self.a.__iadd__(to_add)
    self.a = self.a.__isub__(to_sub)
    

    so the first time one is used, the class attribute is accessed on the RHS, but a new instance attribute is then created, and that instance attribute shadows the class attribute in all future calls.

    If you want to modify a class attribute via an instance, you need to be explicit about it. One possible solution:

    type(self).a += to_add
    

    Your TensorFlow code doesn't make any assignments, augmented or otherwise. It's simply a method call on whatever self.a resolves to, which is the class attribute. No new instance attribute is ever created.