使用父类的 `__new__` 或 `__init__` 方法为子类的每个对象/实例分配默认属性。

huangapple go评论61阅读模式
英文:

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__使用父类的 `__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__. 使用父类的 `__new__` 或 `__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.

huangapple
  • 本文由 发表于 2023年5月10日 19:58:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/76218135.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定