1

I have two class instances as follows

class Foo: 
    def __init__(self, a = 5): 
        self.a = a
f1 = Foo()
f2 = Foo()

Now when I compare the two, f1 is f2 evaluates to False obviously. However, f1.a is f2.a is True. This is a big problem when a is a list like object. How does this happen and how do I get around this (without hardcoding a's value inside __init__)?

I am aware of this and this questions. But they do not answer how I am getting this behavior with no class variable a.

martineau
  • 119,623
  • 25
  • 170
  • 301
Teshan Shanuka J
  • 1,448
  • 2
  • 17
  • 31
  • 1
    See https://stackoverflow.com/questions/306313/is-operator-behaves-unexpectedly-with-integers – Thierry Lathuille Apr 13 '20 at 16:05
  • @ThierryLathuille in my use case it is `a=np.zeros(3)` in the constructor arguments. `np.zeros(3) is np.zeros(3)` is `False`. So still does not explain the behavior. – Teshan Shanuka J Apr 13 '20 at 16:08
  • 1
    @TeshanShanukaJ function defaults are only evaluated once, so yes, you have to move the default inside `__init__` rather than in the signature. See https://stackoverflow.com/questions/1132941/least-astonishment-and-the-mutable-default-argument – Alex Hall Apr 13 '20 at 16:09
  • In both cases f1.a and f2.a value is 5. So the result should be true. If you change it or if you initialize it with another value it won't be true. – fernand0 Apr 13 '20 at 16:11
  • "Never use `is` to compare numbers.".I saw it in the Thierry's link. – jizhihaoSAMA Apr 13 '20 at 16:12
  • @AlexHall Got it. That what I was trying to figure out – Teshan Shanuka J Apr 13 '20 at 16:12

2 Answers2

3

Function defaults get evaluated once. To see this in action, use random.

In [163]: class Foo:  
 ...:     def __init__(self, a =random.random()):  
 ...:         self.a = a 
 ...: f1 = Foo() 
 ...: f2 = Foo()                                                                                 

In [164]:                                                                                            

In [164]: f1.a                                                                                       
Out[164]: 0.6880171149267156

In [165]: f2.a                                                                                       
Out[165]: 0.6880171149267156
Chrispresso
  • 3,660
  • 2
  • 19
  • 31
  • A better proof would be to examine the value of `Foo.__init__.__defaults__`, as it's not *impossible* for `random.random()` to return the same value twice in a row :) – chepner Apr 13 '20 at 16:16
0

You can use copy.deepcopy to deep copy object which contains objects...

import copy

class Foo: 
    def __init__(self, a=5): 
        self.a = copy.deepcopy(a)

Of course, for int deepcopy is useless. It is useful for list, dict, etc.

Laurent LAPORTE
  • 21,958
  • 6
  • 58
  • 103