Cod*_*ope 6 python python-dataclasses
是否可以从 中受益dataclasses.field
,特别是对于默认值,但使用自定义构造函数?我知道@dataclass
注释在生成的中设置默认值__init__
,如果我替换它,就不会再这样做了。那么,是否可以替换生成的__init__
,并仍然在内部调用它?
@dataclass
class A:
l: list[int] = field(default_factory=list)
i: int = field(default=0)
def __init__(self, a: Optional[int]): # completely different args than instance attributes
self.call_dataclass_generated_init() # call generated init to set defaults
if a is not None: # custom settings of attributes
i = 2*a
Run Code Online (Sandbox Code Playgroud)
解决方法是定义__new__
而不是覆盖__init__
,但我更愿意避免这种情况。
这个问题非常接近,但答案仅解决作为代码示例给出的特定用例。另外,我不想使用,__post_init__
因为我需要使用__setattr__
这是静态类型检查的问题,并且它无助于调整__init__
无论如何都会采用的参数。
我也不想使用类方法,我真的希望调用者使用自定义构造函数。
这一个也很接近,但它只是解释为什么新的构造函数替换生成的构造函数,而不是关于如何仍然调用后者(还有一个回复建议使用 Pydantic,但我不想子类化BaseModel
,因为这会弄乱我的继承权)。
因此,简而言之,我希望受益于dataclass
具有属性默认值的功能,而不需要繁琐的解决方法。请注意,原始默认值对我来说不是一个选项,因为它设置类属性:
class B:
a: int = 0 # this will create B.a class attribute, and vars(B()) will be empty
l: list[int] = [] # worse, a mutable object will be shared between instances
Run Code Online (Sandbox Code Playgroud)
据我所知,更干净的方法是有一个替代的类方法用作构造函数:这样,数据类将完全按照预期工作,您可以这样做:
from dataclasses import dataclass, field
from typing import Optional
@dataclass
class A:
l: list[int] = field(default_factory=list)
i: int = field(default=0)
@classmethod
def new(cls, a: Optional[int]=0): # completely different args than instance attributes
# creates a new instance with default values:
instance = cls()
# if one wants to have more control over the instance creation, it is possible to call __new__ and __init__ manually:
# instance = cls.__new__(cls)
# instance.__init__()
if a is not None: # custom settings of attributes
i = 2*a
return instance
Run Code Online (Sandbox Code Playgroud)
但是,如果您不需要显式构造函数方法,并且确实需要调用 just A()
,则可以通过创建一个装饰器来完成,该装饰器将在之后应用@dataclass
- 然后它可以移动__init__
到另一个名称。唯一的事情是您的自定义__init__
必须调用另一个名称,否则@dataclass
将不会创建该方法。
def custom_init(cls):
cls._dataclass_generated_init = cls.__init__
cls.__init__ = cls.__custom_init__
return cls
@custom_init
@dataclass
class A:
l: list[int] = field(default_factory=list)
i: int = field(default=0)
def __custom_init__(self, a: Optional[int]): # completely different args than instance attributes
self._dataclass_generated_init() # call generated init to set defaults
if a is not None: # custom settings of attributes
i = 2*a
...
print("custom init called")
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1579 次 |
最近记录: |