英文:
Using parent class `__new__` or `__init__` method to assign default attributes for every object/instance of a sub-class
问题
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)
英文:
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)
答案1
得分: 2
我认为你把这个问题搞得太复杂了。`Car.__init__` 应该执行对*任何*`Car`对象都通用的初始化,不管具体的子类用于实例化对象。
class Car:
def __init__(self):
self.id = uuid.uuid4()
now = datetime.datetime.now()
self.created_at = now
self.modified_at = now
每个子类将定义一个`__init__`方法,该方法首先调用其继承的`__init__`方法,然后执行任何子类特定的初始化操作。
class Honda(Car):
def __init__(self):
super().__init__()
self.something_else = ...
...
civic = Honda()
`civic.id`,`civic.created_at` 和 `civic.modified_at` 由 `Car.__init__` 定义;`civic.something_else` 由 `Honda.__init__` 定义。
英文:
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__
.
答案2
得分: 1
在Python中,你很少需要接触__new__
- 通常只有在你确切知道你在做什么的情况下才会用到。这意味着:如果你有疑问,就使用__init__
。
但说真的,你提到的任务是典型的,通常应该在__init__
中完成。
这其中有一些语义上的历史原因 - 实际上,也有一些很实际的原因,很少有人意识到:
除了其他事情,Python可以直接支持序列化和反序列化几乎任何由用户定义类创建的对象 - 在不让类作者担心的情况下直接实现,毋需额外操作。
而这种方式能够如此有效的其中一个原因是在__new__
和__init__
方法中对对象的构建进行了分离:__new__
应该构建一个对象作为一个“空壳”,而__init__
则是“填充任何在对象初始化时需要的属性”(不是引用,我现在在用我的话来描述)。反序列化机制利用了这一点来创建一个新对象,然后只需将最初序列化的对象的属性填充到新实例中,而无需调用__init__
。在你的例子中,如果__new__
会创建一个ID(可能会在创建时备份到某个注册表或数据库中,或者是一个不可变属性),这个机制就无法正常工作。
英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论