Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/visual-studio/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何忽略传递给数据类的额外参数?_Python_Python 3.x_Python Dataclasses - Fatal编程技术网

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

Python 如何忽略传递给数据类的额外参数?,python,python-3.x,python-dataclasses,Python,Python 3.x,Python Dataclasses,我想创建一个configdataclass,以简化特定环境变量的白名单和访问(键入os.environ['VAR\u NAME']相对于config.VAR\u NAME来说是一件乏味的事情)。因此,我需要忽略我的数据类的\uuuuu init\uuuu函数中未使用的环境变量,但我不知道如何提取默认的\uuuu init\uuuu,以便用一个同样包含*\uuu作为参数之一的函数来包装它 import os from dataclasses import dataclass @dataclass

我想创建一个
config
dataclass
,以简化特定环境变量的白名单和访问(键入
os.environ['VAR\u NAME']
相对于
config.VAR\u NAME
来说是一件乏味的事情)。因此,我需要忽略我的
数据类
\uuuuu init\uuuu
函数中未使用的环境变量,但我不知道如何提取默认的
\uuuu init\uuuu
,以便用一个同样包含
*\uuu
作为参数之一的函数来包装它

import os
from dataclasses import dataclass

@dataclass
class Config:
    VAR_NAME_1: str
    VAR_NAME_2: str

config = Config(**os.environ)

运行此命令会给我带来
TypeError:\uuuuu init\uuuuuuuuuuuuuuuuuuuuuuuu()得到一个意外的关键字参数“SOME\u DEFAULT\u ENV\u VAR”
我只会提供一个显式的
\uuuuuuuuuuuu init\uuuuuuuuuuuuuuuuuuu
。循环体仅设置可识别的值,忽略意外值

不过,请注意,这不会抱怨没有默认值的值丢失,直到稍后

@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)
或者,您可以将筛选的环境传递给默认的
配置

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})

在将参数列表传递给构造函数之前清理它可能是最好的方法。不过,我建议不要编写自己的
\uuuuu init\uuuuu
函数,因为dataclass'
\uuuuuuu init\uuuuu
会做一些其他方便的事情,如果覆盖它,您会丢失这些事情

此外,由于参数清理逻辑与类的行为紧密绑定并返回实例,因此将其放入
类方法中可能是有意义的:

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
可能是性能杀手。当然,如果字典在dataclass中没有一些记录,您需要为它们设置字段默认值

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})

是的,这是我关心的问题,看起来函数有点复杂,有一些检查等(但我只看了一秒钟)。有没有办法把自动生成的函数撕下来包装一下?我也不希望其他环境变量也在其中;你想替换它。也就是说,在调用默认的
\uuuu init\uuuuu
c=Config({k:v代表k,v代表kwargs,如果k在set中(f.name代表f在dataclasses.fields(Config))})之前过滤环境映射)
在初始化实例之前过滤参数非常有效!如果你把这个问题单独回答,我会接受的。最后的代码是:从数据类导入数据类,字段
config=config(**{k:v代表k,v代表os.environ.items(),如果k在set中(f.name代表f在fields(config))}
。不要使用
cls.\uu注释\uu
,使用
dataclass.fields()
这样你就可以内省它们的配置(例如忽略
init=False
字段)但是在这个上下文中您需要
InitVar
s,不是吗?它们也会被
dataclasses.fields()跳过
,所以这里可能还有更多的问题需要解决。@MartijnPieters
cls.\uuuu dataclass\u fields\uuu
InitVar
包含一起工作,并且可以访问
init
字段。不幸的是,映射还包括
ClassVar
字段,并且这些字段的
init
标志没有设置为
False
at不是我的意思。
inspect.signature()
将为您提供一个
signature
实例,它将允许您轻松创建一组可接受的参数名称。