【问题标题】:What's the pythonic way to instantiate multiple objects from **kwargs?从 **kwargs 实例化多个对象的 Pythonic 方法是什么?
【发布时间】:2019-12-31 11:38:53
【问题描述】:

我面临以下问题。我有一堆输入,需要编写一个函数来实例化几个需要这些输入子集的对象(数据类实例)。

所以我有类似的东西:

def create_objects(**kwargs):
    # as you can see some attrs are used in different objects.
    obj1 = Object1(attr1=kwargs.get("attr1"), attr2=kwargs.get("attr2"))
    obj2 = Object2(attr3=kwargs.get("attr3"), attr2=kwargs.get("attr2"))

由于所有这些数据类都需要传递给它们的多个(2 到 10 个)属性,我想做一些魔术,例如检查 Object1 类是否具有在数据类中定义的某个属性:

    obj1 = Object1(**{k: v for k, v in kwargs.items() if hasattr(Object1, k)})

显然这不起作用。有没有办法以python方式做到这一点?

或者有更好的方法吗?

【问题讨论】:

  • obj1和obj2是同一个类的两个实例吗?
  • 这在很大程度上取决于上下文,以及Object1Object2 类是什么,它们是如何实现的,以及你事先对它们了解多少。您可以使用inspect.signature 来检查它们在初始化时使用了哪些参数,在需要高度灵活性的情况下,我之前已经这样做了,但它确实有点代码味道。如果可以,我建议您先尝试重构,除非这需要真正动态。
  • 我现在正在尝试这个:**{ k: v for k, v in kwargs.items() if k in [f.name for f in dataclasses.fields(Object1)] } 它似乎有效,但相当冗长。还有更好的方法吗?
  • @Thecave3 不,完全不同。
  • 这似乎是最好的解决方案。我不认为它过于冗长。如果您不想进行列表理解,您可以使用__dataclass_fields__ 属性,但为了避免多余的单词,这似乎是不可取的。 **{ k: v for k, v in kwargs.items() if k in Object1.__dataclass_fields__}

标签: python python-dataclasses


【解决方案1】:

您可能想看看dacite library

from dacite import from_dict

def create_objects(**kwargs):
    obj1 = from_dict(data_class=Object1, data=kwargs)
    obj2 = from_dict(data_class=Object2, data=kwargs)

只要将Config.strict 设置为默认选项False,它就会自动忽略额外的字典键。

【讨论】:

  • 感谢您的建议,但试图限制对库的依赖...
【解决方案2】:

我实现的当前解决方案是函数内部的以下辅助函数(kwargs 在闭包中):

def get_kwargs_for_class(cls):
    return {
        k: v
        for k, v in kwargs.items()
        if k in [f.name for f in dataclasses.fields(cls)]
    }

这使我能够做到这一点:

    obj1 = Class1(**get_kwargs_for_class(Class1))
    obj2 = Class2(**get_kwargs_for_class(Class2))

这对我来说看起来还不错。

【讨论】:

    【解决方案3】:

    一种解决方案是使用operator.itemgetter

    例如:

    from operator import itemgetter
    from dataclasses import dataclass
    
    @dataclass
    class Object1:
        name: str
        price: float
    
    @dataclass
    class Object2:
        name: str
        title: str
    
    d = {'name':'some name', 'price': 42, 'title': 'some title'}
    
    def create_objects(d):
        i1 = itemgetter(*Object1.__dataclass_fields__)
        i2 = itemgetter(*Object2.__dataclass_fields__)
    
        obj1 = Object1(*i1(d))
        obj2 = Object2(*i2(d))
    
        print(obj1)
        print(obj2)
    
    create_objects(d)
    

    打印:

    Object1(name='some name', price=42)
    Object2(name='some name', title='some title')
    

    【讨论】:

      猜你喜欢
      • 2015-10-27
      • 1970-01-01
      • 2021-08-03
      • 2012-05-02
      • 2012-03-19
      • 2015-04-26
      • 1970-01-01
      • 1970-01-01
      • 2010-11-30
      相关资源
      最近更新 更多