2

I'm defining a class that has a time attribute which, but default, is set as the current UTC time.

import datetime

class Timer:
    def __init__(self, time = datetime.datetime.utcnow()):
        self.time = time
        print('Instance created at', self.time)
        
        
a = Timer()

The problem is that once defined (or when it is imported, if it's within module), this attribute is set forever! All new instances of the class "inherit" the time from the class, evaluated when it was defined, instead of generating its own "current time" when __init__ is called. Why isn't the function to obtaint current UTC time evaluated every time a new instance is created?

Pythonist
  • 1,937
  • 1
  • 14
  • 25
  • 1
    Duplicate of ["Least Astonishment" and the Mutable Default Argument](https://stackoverflow.com/q/1132941/10987432) – Paul M. Feb 19 '21 at 15:04

1 Answers1

3

By defining the default kwarg for time, time is evaluated when the class definition object is added/interpreted see function __defaults__, an immutable tuple.

>>> class Timer:
...     def __init__(self, time = datetime.datetime.utcnow()):
...         self.time = time
...         print('Instance created at', self.time)
... 
>>> Timer.__init__.__defaults__
# notice how the date is already set here
(datetime.datetime(2021, 2, 19, 15, 22, 42, 639808),)

Whereas you're looking to evaluate it when the class object instantiates.

>>> class Timer:
...     def __init__(self, time = None):
...         self.time = time or datetime.datetime.utcnow()
...         print('Instance created at', self.time)
... 
>>> Timer.__init__.__defaults__
(None,)
>>> a = Timer()
Instance created at 2021-02-19 15:04:45.946796
>>> b = Timer()
Instance created at 2021-02-19 15:04:48.313514
jmunsch
  • 22,771
  • 11
  • 93
  • 114
  • 1
    This isn't a scoping issue, at least not in the sense that the term "scope" is usually used in Python. The issue is that a default argument value is evaluated at class definition time, not when the method is called. – chepner Feb 19 '21 at 15:13
  • 1
    Thanks! I have came up to an even simpler example that nicely showcases the issue you raise about how default arguments are fixed at the moment of the function definition: `def givemetime(time = datetime.datetime.now()): print(time)''. This function has exactly the same issue. – Pythonist Feb 19 '21 at 21:51
  • @Onturenio that is a great minimal example. `givemetime.__defaults__` would be evaluated on definition. – jmunsch Apr 09 '21 at 23:28