如何忽略传递给数据 class 的额外参数?
How does one ignore extra arguments passed to a data class?
我想创建一个 config
dataclass
以简化特定环境变量的白名单和访问(输入 os.environ['VAR_NAME']
相对于 config.VAR_NAME
是乏味的).因此,我需要忽略 dataclass
的 __init__
函数中未使用的环境变量,但我不知道如何提取默认的 __init__
以便用例如 a还包括 *_
作为参数之一的函数。
import os
from dataclasses import dataclass
@dataclass
class Config:
VAR_NAME_1: str
VAR_NAME_2: str
config = Config(**os.environ)
运行 这给了我 TypeError: __init__() got an unexpected keyword argument 'SOME_DEFAULT_ENV_VAR'
.
我只想提供一个明确的 __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)
或者,您可以将过滤环境传递给默认 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})
在将参数列表传递给构造函数之前清理参数列表可能是最好的方法。不过,我建议不要编写自己的 __init__
函数,因为 dataclass' __init__
会做一些其他方便的事情,如果覆盖它就会丢失。
此外,由于参数清理逻辑与 class 和 returns 实例的行为紧密相关,因此将其放入 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')
我结合了这两个答案; 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})
我想创建一个 config
dataclass
以简化特定环境变量的白名单和访问(输入 os.environ['VAR_NAME']
相对于 config.VAR_NAME
是乏味的).因此,我需要忽略 dataclass
的 __init__
函数中未使用的环境变量,但我不知道如何提取默认的 __init__
以便用例如 a还包括 *_
作为参数之一的函数。
import os
from dataclasses import dataclass
@dataclass
class Config:
VAR_NAME_1: str
VAR_NAME_2: str
config = Config(**os.environ)
运行 这给了我 TypeError: __init__() got an unexpected keyword argument 'SOME_DEFAULT_ENV_VAR'
.
我只想提供一个明确的 __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)
或者,您可以将过滤环境传递给默认 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})
在将参数列表传递给构造函数之前清理参数列表可能是最好的方法。不过,我建议不要编写自己的 __init__
函数,因为 dataclass' __init__
会做一些其他方便的事情,如果覆盖它就会丢失。
此外,由于参数清理逻辑与 class 和 returns 实例的行为紧密相关,因此将其放入 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')
我结合了这两个答案; 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})