Python Insantiate对象忽略其他参数

时间:2018-12-06 06:18:45

标签: python python-3.x

我经常遇到此问题:

我定义了一个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

3 个答案:

答案 0 :(得分:3)

通常,您将只能使用hackey方法。老实说,我无法想到为什么您不能直接提供所需的参数。但是Python具有很多自省/元编程功能,因此您可以一起破解:

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'])

答案 1 :(得分: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商品,那么您应该问自己:为什么我接受一个实例,该实例创建的参数数量错误并限制了函数签名?

总结一下,您有几个选择:

  • 设置固定签名,因为您打算使用此特定参数创建类(以及为什么不在assert定义中添加很多__init__),然后您需要使用正确的参数设置调用您的类实例;
  • 利用*args**kwargs的优势来设置变量签名,而不必理会那些多余的参数(如果您的数据来自另一个JSON格式的应用程序,认为我们可以说这样做是安全的);
  • 创建一个工厂,该实例将在实例化之前对参数进行预处理,但这将在代码上添加一些抽象层,这将不会给IMO带来很大的好处;
  • 我没有考虑过的另一种入侵方式...

回顾Python的禅宗:

import this
  • 美丽胜过丑陋。
  • 显式优于隐式。
  • 简单胜于复杂。
  • 复杂胜于复杂。
  • 可读性计数。
  • 特殊情况还不足以打破规则。
  • 应该有一种-最好只有一种-显而易见的方式。
  • 如果难以解释实现方式,那是个坏主意。
  • 如果实现方式易于解释,则可能是个好主意。

答案 2 :(得分:0)

我现在正在使用第三方库pydantic,该库专门解决了数据验证和输出一致性的问题。

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

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

这不仅实现了更多的验证,同时也是一种更加严格的方法。