冷冻数据级Pathlib。类似Path的初始化

发布于 2025-01-21 10:58:44 字数 1210 浏览 1 评论 0原文

我有这个数据级:

@dataclass(frozen=True)
class CacheSchema:
    id_key: str
    snapshot_key: str | None
    version: str = Info.Versions.schema_cache

我想以相同的方式初始化它pathlib.path.path做到了,因此要么传递所需的参数或已经初始化的cacheschemachema对象。从我有限的理解来看,我认为我必须自定义__ 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)

我的逻辑是:如果传递给定args的普通参数<代码> __ INT __ INT __(),否则请解开现有对象并回忆__ __ new __() with打开的值。

我的问题是cls .__ new __(Cls,args [0] .id_key,args [0] .snapshot_key,args [0] .version)不做我以为会做的事( __新的__递归但具有不同的args)。

运行此

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'

I have this dataclass:

@dataclass(frozen=True)
class CacheSchema:
    id_key: str
    snapshot_key: str | None
    version: str = Info.Versions.schema_cache

and I would like to initialize it the same way pathlib.Path does it, so either pass the required arguments or an already initialized CacheSchema object. From my limited understanding I figured I would have to customize __new__(), so I did, however I can't do it exactly as Path does it because the dataclass is frozen and I'm not supposed to change the values after creation. So I came up with this:

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)

My logic was: if normal arguments are passed call __init__() with the given args, otherwise unpack the existing object and recall __new__() with the unpacked values.

My issue is that cls.__new__(cls, args[0].id_key, args[0].snapshot_key, args[0].version) doesn't do what I thought it would do (call __new__ recursively but with different args).

Running this

schema= CacheSchema('a', 'b', 'c')
schema2 = CacheSchema(schema)

raises

File "D:/x/y/z/main.py", line 10, in <module>
    schema2 = CacheSchema(schema)
TypeError: CacheSchema.__init__() missing 1 required positional argument: 'snapshot_key'

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

ˉ厌 2025-01-28 10:58:44

__ Init ____新__之后调用。

创建本质上是:

@classmethod
def __call__(cls, *args, **kwargs):
    self = cls.__new__(cls, *args, **kwargs)
    cls.__init__(self, *args, **kwargs)
    return self

因此,您不能重新包装args
另请参见: python:override __init__ ___new__

pathlib.path.path.path.path.path.path.path.path.pathlib. like intantiation

与您的数据级不同,pathlib.path没有带有所需参数的__ INIT __方法。

它的__新__方法调用其他类方法_FROM_PARTS 1 1 dos self = object .__ new __ new __(cls),然后设置self上的属性直接。

仅使用__新__方法在数据级上进行类似的操作是:

@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/cpython/issues/85281

pathlib.path.path.path.path-path-path-path-like-like 初始评估

您需要包装 __ Init __功能由@DataClass添加。

您可以使用另一个装饰器进行此操作:

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

或者您可以在__ __新__中包装__ INT __ INT __

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)

__init__ is called after __new__.

Creation is essentially:

@classmethod
def __call__(cls, *args, **kwargs):
    self = cls.__new__(cls, *args, **kwargs)
    cls.__init__(self, *args, **kwargs)
    return self

Therefore, you cannot repack args.
Also see: Python: override __init__ args in __new__

pathlib.Path-like instantiation

Unlike your dataclass, pathlib.Path doesn't have an __init__ method with required parameters.

Its __new__ method calls another class method _from_parts1 that does self = object.__new__(cls) and then sets attributes on self directly.

To do something like this on your dataclass with only the __new__ method would be:

@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 This has other issues: https://github.com/python/cpython/issues/85281

pathlib.Path-like initialisation

You need to wrap the __init__ function added by @dataclass.

You can do this with another decorator:

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

Usage:

@init_accept_instance
@dataclass(frozen=True)
class CacheSchema:
    id_key: str
    snapshot_key: str
    version: str = Info.Versions.schema_cache

Or you can wrap __init__ in __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)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文