Python 实例化对象忽略额外参数

3
我经常遇到这个问题:
我定义了一个Python类,例如:
class A(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b

然后我有一些具有附加属性的数据,我需要将其转换为此对象。

例如: data={"a": 1, "b": 2, "c": 3}

如何创建对象A,忽略数据中任何额外的属性,可以使用A(**data)这样的语法或使用通用帮助方法,例如toObject(A, **data),而不需要在类A的定义中包含**kwargs

你能更具体地说明你期望的结果是什么吗?你打算创建 c 吗? - jlandercy
为什么不直接提供参数呢?A(data['a'],data['b']) - juanpa.arrivillaga
@juanpa.arrivillaga 那恰恰是我想要避免的代码。 - hangc
@user113531,那你为什么定义你的类不接受 **kwargs 参数呢? - juanpa.arrivillaga
不是必须的,但对于此案例而言是有用的,不是吗?那将是接受包含任意数据的**kwargs的类的简单方法。其他选项都像hacky as heck。 - juanpa.arrivillaga
显示剩余2条评论
3个回答

3

一般情况下,你只能使用hackey方法。老实说,我想不出为什么你不能直接提供所需的参数。但是Python具有很多内省/元编程功能,因此你可以组合一些东西来hack:

import inspect

def instantiate(cls, payload):
    params = inspect.signature(cls).parameters
    return cls(**{k:payload[k] for k in params})

my_a = instantiate(A, data)

但实际上,这只是代码异味。

你应该使用 A(data['a'],data['b'])


我正要加载“inspect”模块。 - jlandercy
1
@jlandercy 是的,新的 inspect.signature 很不错,直到现在我才开始尝试使用它。 - juanpa.arrivillaga
我完全同意。 - jlandercy
除非你将其视为代码异味,否则你的答案完全解决了OP的最新版本问题。此外,它干净、简短、通用且仅大约3行代码(感谢Python)。 - jlandercy

1

我现在正在使用第三方库pydantic,它专门解决数据验证和输出确认的问题。

在这种情况下,我想确保输入数据符合类A的签名。可以通过以下方式实现:

  1. 定义一个模型,例如AModel,使用来自pydantic的BaseModel匹配类的输入参数。
  2. 在实例化类之前,通过模型传递data负载,A(**AModel(**data).dict())(pydantic模型的默认行为忽略额外属性)

这样既可以实现附加验证,又是一种更有纪律性的方法。


1
如果您同意使用**kwargs,那么这很简单:
class A:
    def __init__(self, a, b, **kwargs):
        self.a = a
        self.b = b
        # Ignore those two lines below if no need of extra args:
        if kwargs.get("update", False):
            self.__dict__.update(kwargs)

然后A(**data)会忽略额外的参数,而A(**data, update=True)会使用额外的参数(包括update)更新实例。
这只是关于function signature的问题,**kwargs确保它会收集所有额外的参数,并适用于任何使用或误用,这样您就永远不会获得:
TypeError: __init__() got an unexpected keyword argument 'c'

如果您不想使用 **kwargs 这个便利的东西,那么您应该问自己:为什么我要接受使用错误数量参数创建实例并同时限制函数签名呢? 总结一下,您有几个选项:
  • 设置一个固定签名,因为您打算使用这些特定的参数来创建类(为什么不在__init__定义中添加大量的assert),然后您需要使用正确的参数设置调用类实例;
  • 设置一个可变签名,利用*args**kwargs的优势,只需不关心那些额外的参数(如果您的数据来自JSON格式的另一个应用程序,我认为我们可以这样安全地进行);
  • 创建一个工厂,在实例化之前预处理参数,但这将在您的代码中添加一些抽象层,我认为不会带来很大的好处;
  • 我没有考虑过的其他方法……
回想一下 Python 之禅:
import this
  • 美观胜于丑陋。
  • 明确胜于含蓄。
  • 简单胜于复杂。
  • 复杂胜于混乱。
  • 易读性至关重要。
  • 特殊情况并不足以打破规则。
  • 应该有一种 -- 最好只有一种 -- 显而易见的方法来完成它。
  • 如果实现难以解释,那就是个坏主意。
  • 如果实现容易解释,那可能是一个好主意。

不错的回答,尽管你可能需要向op解释kwargs.get("update", False) - Grijesh Chauhan
1
OP明确表示:“在A的定义中不包括**kwargs”。 - juanpa.arrivillaga
@juanpa.arrivillaga 要么我没看到,要么在我回答的同时OP已经编辑了内容。 - jlandercy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接