【问题标题】:Pythonic way to check if a dataclass field has a default value检查数据类字段是否具有默认值的 Pythonic 方法
【发布时间】:2019-05-04 12:19:17
【问题描述】:

我最近一直在使用 python 3.7,并且正在寻找利用新数据类的方法。基本上我有一个方法可以遍历数据类字段并检查它们是否具有默认值:

from dataclasses import fields, MISSING

@classmethod
def from_json(cls)
    datacls_fields = fields(cls)
    for field in datacls_fields:
        if  (field.default != MISSING):
            #...

但在官方文档中,它说:

MISSING 值是用于检测的标记对象 如果提供了 default 和 default_factory 参数。这 使用哨兵是因为 None 是默认的有效值。 无代码 应该直接使用 MISSING 值。

有人知道更好/更pythonic的方法吗?

【问题讨论】:

  • 也许pythonic的问题可能是:为什么需要找出具有默认值的字段?
  • 我认为反对使用MISSING 的警告只是说它不应该对您的代码有意义。例如,如果您的某个数据类有一个默认值为MISSING 的字段,则可能会导致各种奇怪的行为。在这样的检查中使用它不会导致任何问题。
  • 我认为,正如@PatrickHaugh 所说,您的代码是正确的,并没有真正做任何危险的事情。如果有的话,为了让这更 Pythonic,我会使用 is not MISSING 并在条件周围去掉括号。

标签: python python-3.x python-dataclasses


【解决方案1】:

这是dataclasses.py中python源码中MISSING的定义:

# A sentinel object to detect if a parameter is supplied or not.  Use
# a class to give it a better repr.
class _MISSING_TYPE:
    pass
MISSING = _MISSING_TYPE()

定义很清楚,它的用例只是检查是否提供了参数,并区分None的值和未提供的值:

def my_func(a=MISSING):
    if a is not MISSING:
        # a value has been supplied, whatever his value

所以在您的代码中使用它来进行价值比较是完全可以的。通过告诉任何代码都不应直接使用 MISSING 值,他们只是警告我们此变量没有特定用途(用于比较的其他用途)并且不应在代码中使用以避免意外行为。

您应该更新您的代码以使用更 Pythonic 的语法 is not MISSING

from dataclasses import fields, MISSING

@classmethod
def from_json(cls)
    datacls_fields = fields(cls)
    for field in datacls_fields:
        if field.default is not MISSING:

【讨论】:

    【解决方案2】:

    我认为 pythonic 解决方案依赖于使用魔法__dict__(文档:https://docs.python.org/3/library/stdtypes.html#object.__dict__)。

    class Foo:
    def __init__(self, a=1, b=2, c=3):
        self.a = a
        self.b = b
        self.c = c
    
    x = Foo()
    y = Foo(a=5)
    print(x.__dict__ == Foo().__dict__)  # True
    print(y.__dict__ == Foo().__dict__)  # False
    

    基本上,在对象实例上使用时,它会返回一个字段-值对(键-值)的字典。

    为了检查这些是否为默认值,我只需将给定实例上的这个字典与在默认无名对象上创建的同一个字典进行比较。

    代码可能更具解释性。

    附:这是所有字段是否都有默认值的答案。如果您需要确切哪些字段没有默认值的信息,这是我提出的解决方案(基于与上述相同的逻辑,但不是比较字典是否相等,而是找出它们之间的区别):

    # Need the actual fields back?
    x_dict = x.__dict__
    y_dict = y.__dict__
    default = Foo().__dict__
    print(dict(set(x_dict.items()) - set(default.items())))
    print(dict(set(y_dict.items()) - set(default.items())))
    

    使用此代码时的输出:

    {}
    {'a': 5}
    

    请参阅How to get the difference between two dictionaries in Python?,了解有关如何查找两个字典之间差异的更多信息。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-24
      • 2011-03-27
      • 2021-09-21
      • 2014-01-07
      • 1970-01-01
      相关资源
      最近更新 更多