How does one ignore extra arguments passed to a data class?

后端 未结 3 1319
南旧
南旧 2020-12-11 01:37

I\'d like to create a config dataclass in order to simplify whitelisting of and access to specific environment variables (typing os.environ[\

相关标签:
3条回答
  • 2020-12-11 02:12

    Cleaning the argument list before passing it to the constructor is probably the best way to go about it. I'd advice against writing your own __init__ function though, since the dataclass' __init__ does a couple of other convenient things that you'll lose by overwriting it.

    Also, since the argument-cleaning logic is very tightly bound to the behavior of the class and returns an instance, it might make sense to put it into a classmethod:

    from dataclasses import dataclass
    import inspect
    
    @dataclass
    class Config:
        var_1: str
        var_2: str
    
        @classmethod
        def from_dict(cls, env):      
            return cls(**{
                k: v for k, v in env.items() 
                if k in inspect.signature(cls).parameters
            })
    
    
    # usage:
    params = {'var_1': 'a', 'var_2': 'b', 'var_3': 'c'}
    c = Config.from_dict(params)   # works without raising a TypeError 
    print(c)
    # prints: Config(var_1='a', var_2='b')
    
    0 讨论(0)
  • 2020-12-11 02:29

    I would just provide an explicit __init__ instead of using the autogenerated one. The body of the loop only sets recognized value, ignoring unexpected ones.

    Note that this won't complain about missing values without defaults until later, though.

    @dataclass
    class Config(init=False):
        VAR_NAME_1: str
        VAR_NAME_2: str
    
        def __init__(self, **kwargs):
            names = set([f.name for f in dataclasses.fields(self)])
            for k, v in kwargs.items():
                if k in names:
                    setattr(self, k, v)
    

    Alternatively, you can pass a filtered environment to the default Config.__init__.

    field_names = set(f.name for f in dataclasses.fields(Config))
    c = Config(**{k:v for k,v in os.environ.items() if k in field_names})
    
    0 讨论(0)
  • 2020-12-11 02:31

    I used a combination of both answers; setattr can be a performance killer. Naturally, if the dictionary won't have some records in the dataclass, you'll need to set field defaults for them.

    from __future__ import annotations
    from dataclasses import field, fields, dataclass
    
    @dataclass()
    class Record:
        name: str
        address: str
        zip: str = field(default=None)  # won't fail if dictionary doesn't have a zip key
    
        @classmethod
        def create_from_dict(cls, dict_) -> Record:
            class_fields = {f.name for f in fields(cls)}
            return Record(**{k: v for k, v in dict_.items() if k in class_fields})
    
    0 讨论(0)
提交回复
热议问题