如何忽略传递给数据类的额外参数?

Cal*_*ian 8 python python-3.x python-dataclasses

我想创建一个config dataclass,以简化对特定环境变量的白名单和访问(os.environ['VAR_NAME']相对于而言,输入代码很麻烦config.VAR_NAME)。因此,我需要无视我的未使用的环境变量dataclass__init__函数,但我不知道如何提取默认__init__为了与例如,一个函数,还包括将其包装*_为一体的论据之一。

import os
from dataclasses import dataclass

@dataclass
class Config:
    VAR_NAME_1: str
    VAR_NAME_2: str

config = Config(**os.environ)
Run Code Online (Sandbox Code Playgroud)

运行这个给了我TypeError: __init__() got an unexpected keyword argument 'SOME_DEFAULT_ENV_VAR'

che*_*ner 16

我只想提供一个明确的__init__而不是使用自动生成的。循环体只设置可识别的值,忽略意外的值。

请注意,直到稍后才会抱怨没有默认值的缺失值。

@dataclass(init=False)
class Config:
    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)
Run Code Online (Sandbox Code Playgroud)

或者,您可以将过滤后的环境传递给默认的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})
Run Code Online (Sandbox Code Playgroud)

  • 您不想包装自动生成的函数;你想更换它。也就是说,你总是可以在调用默认的 `__init__` 之前过滤环境映射:`c = Config({k:v for k,v in kwargs if k in set(f.name for f in dataclasses.fields(配置))})` (2认同)
  • 在初始化实例之前过滤参数效果很好!如果你把它变成一个单独的答案,我会接受它。我最终得到的代码:`from dataclasses import dataclass, fields` ... `config = Config(**{k:v for k,v in os.environ.items() if k in set(f.name for f在字段(配置))}`。 (2认同)

Arn*_*rne 7

在将参数列表传递给构造函数之前对其进行清理可能是解决此问题的最佳方法。但我建议不要编写自己的__init__函数,因为数据类会__init__做一些其他方便的事情,而覆盖它们会使其失去作用。

另外,由于参数清除逻辑与类的行为紧密绑定并返回实例,因此将其放入可能是有道理的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')
Run Code Online (Sandbox Code Playgroud)

  • 我不是这个意思。`inspect.signature()` 将为您提供一个 `Signature` 实例,它可以让您轻松创建一组可接受的参数名称。 (2认同)

Dou*_*oug 7

我结合使用了两个答案;setattr可能会成为性能杀手。当然,如果字典的数据类中没有某些记录,您需要为它们设置字段默认值。

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})
Run Code Online (Sandbox Code Playgroud)