【问题标题】:How can I access nested nullable values of a Pydantic object without duplication?如何在不重复的情况下访问 Pydantic 对象的嵌套可为空值?
【发布时间】:2021-09-27 15:08:27
【问题描述】:

我从 API 收到这样的结构:

from pydantic import BaseModel

class Country(BaseModel):
    code: Optional[str] = None

class Address(BaseModel):
    street: str
    country: Optional[Country] = None

class Person(BaseModel):
    firstName: str
    lastName: str
    address: Optional[Address] = None

如果我想获取国家代码,我需要这样做:

def get_country_code(p: Person):
    if p.address and p.adress.country:
        return p.address.country.code
    return None

如您所见,当我的深度为 3 时,我需要复制 p.address 3 次。在我的实际情况中,数据甚至更加嵌套。

这非常烦人,并且使代码更难阅读。我希望有这样的事情:

def get_country_code(p: Person):
    return p.?address.?country.code

属性访问前面的?本质上的意思是“如果后面的属性存在,取它。如果不存在,返回None

可能的解决方案 #1:转换为 dict

如果是字典,我至少可以这样做:

def get_country_code(p: Person):
    return p.get("address", {}).get("country", {}).get("code", None)

因此,我目前正在考虑将其转换回字典...这有点可悲。有没有更好的方法来访问嵌套属性?

可能的解决方案 #2:Dict 默认值 + 修改 BaseModel

我可以将 Optional[Address] 更改为 Union[Address, Dict[str, Any]] 并将默认值设为空字典。但是,Pydantic BaseModel 没有.get(some_str)。因此我需要对其进行修改。

问题

我无法改变数据结构嵌套如此之多或存在许多可为空字段的事实。我只能接受。

有没有办法在不失去编辑器/mypy 支持的情况下拥有.get("attribute", "fallback") 语法?

【问题讨论】:

  • 您希望存在的 (p.?address.?country.code) 确实存在。它叫getattr
  • @Shinratensei 哦,对了,我完全忘记了这一点????但是,当我使用 getattr 时,我仍然失去了对 mypy / editor 的支持。
  • 嗯嗯,你可以随时将cast()结果发送到Optional[str]

标签: python pydantic


【解决方案1】:

我通常只使用getattr。它不是很漂亮,但它可以完成工作

return getattr(getattr(p.address, 'country', None), 'code', None)

【讨论】:

    【解决方案2】:

    @Matias Cicero's answer 的基础上,如果您的数据结构像您所说的那样严重嵌套,那么这样的内容可能会变得更具可读性。但我什至猜想,一些简洁的库已经包含了一种迭代应用相同函数的方法。

    attrs = ["address", "country", "code"]
    
    def get_country_code(p: Person):
        i = 0
        while p := getattr(p, attrs[i], None) and i < len(attrs):
            i += 1
        return p
    

    【讨论】:

      【解决方案3】:

      如果您可以使用第三方模块,我建议您使用pydash

      import pydash as _
      
      ...
      
      def get_country_code(p: Person):
          return _.get(p, 'address.country.code')
      

      【讨论】:

        猜你喜欢
        • 2012-09-02
        • 2019-10-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-11-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多