简体   繁体   中英

Is there an alias or name parameter for dataclass arguments?

I have a class that accepts a lot of params, and in init method I'm loading them in differently named params. I know it might be a bad design or whatever, but I can't change that right now. I've tried a lot of stuff but nothing really did the thing. Is it possible to do it within dataclasses?

class MyClass:
    def __init__(self, vp):
        self.viewport = vp

I know that this is not intended behaviour of dataclass but I'm wondering if it's possible to make some workaround.

What I'd like to have is this mapping within dataclass:

@dataclass
class MyClass:
   viewport:str = "" # this should get the value from vp argument if possible

so when I call:
mc = MyClass(vp="foo")
print(mc) should return (MyClass(viewport="foo"))

Would this work?

from dataclasses import dataclass


arg_dict = {'vp':'viewport'}

@dataclass
class RealMyClass():
    viewport: str = ''
    some_other_field: int = 10
        
        
class MyClass(RealMyClass):
    def __init__(self, **kwargs):
        super().__init__(**{arg_dict[arg]:value for arg, value in kwargs.items()})


print(MyClass(vp='foo'))
MyClass(viewport='foo', some_other_field=10)

You could use an InitVar for the vp alias.

from dataclasses import dataclass, InitVar


@dataclass
class MyClass:
    viewport: str = ""  # this should get the value from vp argument if possible
    vp: InitVar[str] = None

    def __post_init__(self, vp):
        if vp:
            self.viewport = vp


mc = MyClass(vp="foo")

If you need to define many aliases, you could also write your own decorator that uses Field.metadata like so:

from dataclasses import dataclass, field, fields
from functools import wraps


def _wrap_init(original_init):
    @wraps(original_init)
    def __init__(self, *args, **kwargs):
        aliases = {}
        for field in fields(self):
            alias = field.metadata.get("alias")
            if alias is not None:
                value = kwargs.pop(alias)
                aliases[field.name] = value

        original_init(self, *args, **kwargs)

        for name, value in aliases.items():
            setattr(self, name, value)

    return __init__


def aliased(cls):
    original_init = cls.__init__
    cls.__init__ = _wrap_init(original_init)
    return cls


@aliased
@dataclass
class MyClass:
    viewport: str = field(default="", metadata={"alias": "vp"})


mc = MyClass(vp="foo")

I don't see any context where this would be a good idea, but you can do it in this way:

from dataclasses import dataclass, field, InitVar
@dataclass
class MyClass:
    vp: InitVar[str]
    viewport: str = field(init=False)
    def __post_init__(self,vp):
        self.viewport = vp
        
MyClass(vp='hola')

Which will return:

MyClass(viewport='hola')

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM