I am trying to create a parent class - Car that would be able to automatically assign a unique id (using uuid.uuid4()), a created_at attribute and an updated_at attribute (both using the datetime and dateutil modules) to any object that would be created through the sub-classes of my parent class.

For example: When an object (honda_civic()) of my sub-class Honda, which inherits from my Car parent class, is created, honda_civic would be automatically assigned a unique id, then it will also have a time created and time updated, as well as the other attributes and methods defined in the Honda sub-class.

Based on my research so far, I know that I would need to define these attributes inside either of the __new__ or __init__ methods of my parent class Car (I stand to be corrected). However, I'm struggling to figure out exactly which of them should have the definitions.

I know that the __new__ method controls object creation while the __init__ method controls object instantiation. My confusion now is that if I define the attributes under the __init__ method, would that not mean that each time the object is created the attributes would have to be passed as arguments? (which is definitely not ideal). What I need help with now is figuring out the syntax of defining the attributes under the __new__ method because I am a bit confused by the def __new__(cls, *args, **kwargs) prototype definition of the __new__ method and how it can accommodate the definitions; can I pass the attributes to the __new__ prototype? like so: def __new__(cls, id, created_at, updated_at)

2

There are 2 best solutions below

1
chepner On BEST ANSWER

I think you are overcomplicating this. Car.__init__ should perform initialization that is common to any Car, regardless of the specific subclass used to instantiated the object.

class Car:
   def __init__(self):
       self.id = uuid.uuid4()
       now = datetime.datetime.now()
       self.created_at = now
       self.modified_at = now

Each subclass will define an __init__ method that first calls its inherited __init__ method, then does any subclass-specific intialization.

class Honda(Car):
    def __init__(self):
        super().__init__()
        self.something_else = ...
        ...

civic = Honda()

civic.id, civic.created_at, and civic.modified_at are defined by Car.__init__; civic.something_else is defined by Honda.__init__.

0
jsbueno On

In Python you will rarely need to touch __new__ - and usually only when you really know what you are doing. Which means: when in doubt, use __init__. :-)

But seriously, the tasks you mention are typical, and meant to be done on __init__.

There are a few semantic and historic reasons for it - but actually, also, very practical reasons few people are aware of:

Among other things Python can do, there is out-of-the box support for serializing and de-serializing almost any object created by a user-defined class - out of the box, without the class author never worrying about it.

And one of the reasons that works in such a fine way is this separation of the construction of an object in the __new__ and __init__ methods: __new__ is supposed to build the object as an "empty shell", and __init__ to "fill in any attributes needed at object initialization" (not quotes, I am wording these now). The de-serialization mechanism uses that to create a new object, and them just filling in the attributes of the originally serialized objects into the new instance, without calling __init__. In your example, if __new__ would create an ID (which might be backed up in some registry or DB at creation, or be an immutable attribute), this mechanism would not work.