冻结数据类 pathlib.Path-like 初始化
Frozen dataclass pathlib.Path-like initialization
我有这个数据类:
@dataclass(frozen=True)
class CacheSchema:
id_key: str
snapshot_key: str | None
version: str = Info.Versions.schema_cache
我想像 pathlib.Path
一样初始化它,所以要么传递所需的参数,要么传递一个已经初始化的 CacheSchema
对象。根据我有限的理解,我认为我必须自定义 __new__()
,所以我做了,但是我不能完全按照 Path
那样做,因为数据类被冻结,我不应该改变创建后的值。所以我想到了这个:
def __new__(cls, *args, **kwargs):
if len(args) == 1 and type(args[0]) is cls:
return cls.__new__(cls, args[0].id_key, args[0].snapshot_key, args[0].version)
return super(CacheSchema, cls).__new__(cls)
我的逻辑是:如果传递了普通参数,则使用给定的参数调用 __init__()
,否则解压缩现有对象并使用解压缩的值调用 __new__()
。
我的问题是 cls.__new__(cls, args[0].id_key, args[0].snapshot_key, args[0].version)
没有按照我的预期执行(递归调用 __new__
但参数不同)。
运行这个
schema= CacheSchema('a', 'b', 'c')
schema2 = CacheSchema(schema)
加注
File "D:/x/y/z/main.py", line 10, in <module>
schema2 = CacheSchema(schema)
TypeError: CacheSchema.__init__() missing 1 required positional argument: 'snapshot_key'
__init__
在 __new__
之后被调用。
创作本质上是:
@classmethod
def __call__(cls, *args, **kwargs):
self = cls.__new__(cls, *args, **kwargs)
cls.__init__(self, *args, **kwargs)
return self
因此,您不能重新打包 args
。
另见:Python: override __init__ args in __new__
pathlib.Path-like 实例化
与您的数据class不同,pathlib.Path
没有带有必需参数的 __init__
方法。
它的 __new__
方法调用另一个 class 方法 _from_parts
1 执行 self = object.__new__(cls)
然后在 self
直接。
仅使用 __new__
方法对您的数据class 执行类似操作将是:
@dataclass(frozen=True)
class CacheSchema:
id_key: str
snapshot_key: str
version: str = ""
def __new__(cls, *args, **kwargs):
if not hasattr(cls, '_init'):
setattr(cls, '_init', cls.__init__)
delattr(cls, '__init__')
self = object.__new__(cls)
if len(args) == 1 and type(args[0]) is cls:
cls._init(self, args[0].id_key, args[0].snapshot_key, args[0].version)
return self
cls._init(self, *args, **kwargs)
return self
1 这还有其他问题:https://github.com/python/cpython/issues/85281
pathlib.Path-like 初始化
您需要包装 @dataclass
添加的 __init__
函数。
你可以用另一个装饰器来做到这一点:
def init_accept_instance(cls):
init = cls.__init__
def __init__(self, *args, **kwargs):
if len(args) == 1 and type(args[0]) is cls:
args, kwargs = (), args[0].__dict__
init(self, *args, **kwargs)
cls.__init__ = __init__
return cls
用法:
@init_accept_instance
@dataclass(frozen=True)
class CacheSchema:
id_key: str
snapshot_key: str
version: str = Info.Versions.schema_cache
或者您可以将 __init__
包装在 __new__
中:
from functools import wraps
@dataclass(frozen=True)
class CacheSchema:
id_key: str
snapshot_key: str
version: str = ""
def __new__(cls, *_, **__):
if not hasattr(cls.__init__, '__wrapped__'):
@wraps(cls.__init__)
def __init__(self, *args, **kwargs):
if len(args) == 1 and type(args[0]) is cls:
args = (args[0].id_key, args[0].snapshot_key, args[0].version)
cls.__init__.__wrapped__(self, *args, **kwargs)
setattr(cls, '__init__', __init__)
return super().__new__(cls)
我有这个数据类:
@dataclass(frozen=True)
class CacheSchema:
id_key: str
snapshot_key: str | None
version: str = Info.Versions.schema_cache
我想像 pathlib.Path
一样初始化它,所以要么传递所需的参数,要么传递一个已经初始化的 CacheSchema
对象。根据我有限的理解,我认为我必须自定义 __new__()
,所以我做了,但是我不能完全按照 Path
那样做,因为数据类被冻结,我不应该改变创建后的值。所以我想到了这个:
def __new__(cls, *args, **kwargs):
if len(args) == 1 and type(args[0]) is cls:
return cls.__new__(cls, args[0].id_key, args[0].snapshot_key, args[0].version)
return super(CacheSchema, cls).__new__(cls)
我的逻辑是:如果传递了普通参数,则使用给定的参数调用 __init__()
,否则解压缩现有对象并使用解压缩的值调用 __new__()
。
我的问题是 cls.__new__(cls, args[0].id_key, args[0].snapshot_key, args[0].version)
没有按照我的预期执行(递归调用 __new__
但参数不同)。
运行这个
schema= CacheSchema('a', 'b', 'c')
schema2 = CacheSchema(schema)
加注
File "D:/x/y/z/main.py", line 10, in <module>
schema2 = CacheSchema(schema)
TypeError: CacheSchema.__init__() missing 1 required positional argument: 'snapshot_key'
__init__
在 __new__
之后被调用。
创作本质上是:
@classmethod
def __call__(cls, *args, **kwargs):
self = cls.__new__(cls, *args, **kwargs)
cls.__init__(self, *args, **kwargs)
return self
因此,您不能重新打包 args
。
另见:Python: override __init__ args in __new__
pathlib.Path-like 实例化
与您的数据class不同,pathlib.Path
没有带有必需参数的 __init__
方法。
它的 __new__
方法调用另一个 class 方法 _from_parts
1 执行 self = object.__new__(cls)
然后在 self
直接。
仅使用 __new__
方法对您的数据class 执行类似操作将是:
@dataclass(frozen=True)
class CacheSchema:
id_key: str
snapshot_key: str
version: str = ""
def __new__(cls, *args, **kwargs):
if not hasattr(cls, '_init'):
setattr(cls, '_init', cls.__init__)
delattr(cls, '__init__')
self = object.__new__(cls)
if len(args) == 1 and type(args[0]) is cls:
cls._init(self, args[0].id_key, args[0].snapshot_key, args[0].version)
return self
cls._init(self, *args, **kwargs)
return self
1 这还有其他问题:https://github.com/python/cpython/issues/85281
pathlib.Path-like 初始化
您需要包装 @dataclass
添加的 __init__
函数。
你可以用另一个装饰器来做到这一点:
def init_accept_instance(cls):
init = cls.__init__
def __init__(self, *args, **kwargs):
if len(args) == 1 and type(args[0]) is cls:
args, kwargs = (), args[0].__dict__
init(self, *args, **kwargs)
cls.__init__ = __init__
return cls
用法:
@init_accept_instance
@dataclass(frozen=True)
class CacheSchema:
id_key: str
snapshot_key: str
version: str = Info.Versions.schema_cache
或者您可以将 __init__
包装在 __new__
中:
from functools import wraps
@dataclass(frozen=True)
class CacheSchema:
id_key: str
snapshot_key: str
version: str = ""
def __new__(cls, *_, **__):
if not hasattr(cls.__init__, '__wrapped__'):
@wraps(cls.__init__)
def __init__(self, *args, **kwargs):
if len(args) == 1 and type(args[0]) is cls:
args = (args[0].id_key, args[0].snapshot_key, args[0].version)
cls.__init__.__wrapped__(self, *args, **kwargs)
setattr(cls, '__init__', __init__)
return super().__new__(cls)