pydantic: 使用参数化默认值的BaseSettings

huangapple go评论102阅读模式
英文:

pydantic: BaseSettings with parametrized default values

问题

似乎无法在 pydantic 的 BaseSettings 超类上创建 __init__ 函数。

我试图实现这个(注意:为了演示,这段代码在 pydantic 中不受支持):

class MySettings(BaseSettings):
    
    def __init__(self, foo: str):
        
        self.p1 = f"{foo}_param_name1"  # <- trying to achieve this 
        self.p2 = f"{foo}_param_name2"  # <- trying to achieve this

    redis_host: Optional[str] = "asdf"

settings = MySettings(foo="test")
assert settings.redis_host == "asdf"
assert settings.p1 == "test_param_name1"  # 这将引发 p1 不存在的 ValueError
assert settings.p2 == "test_param_name2"

到目前为止,我找到的最接近的方法是使用 create_model,但在 BaseSettings 的上下文中并没有讨论这一点:https://docs.pydantic.dev/usage/models/#dynamic-model-creation

是否有一种方法可以实现一个基于参数设置某些值的 BaseSettings 超类?

以下方法似乎相当糟糕,而且还会极大地混淆代码编辑器:

def retrofit(class_instance: BaseSettings, foo: str):
      setattr(class_instance, "p1", f"{foo}_param_name1")

这也有效,但会导致许多 linting 错误,而且还会混淆编辑器(似乎不知道 p1 是一个参数):

def default_settings(foo: str) -> Type[BaseSettings]:
    class DefaultSettings(BaseSettings):
        
        p1 = f"{foo}_param_name1"
        redis_host: Optional[str] = "asdf"

    return DefaultSettings

MyDefaultSettings: Type[BaseSettings] = default_settings("test")

class SuperSettings(MyDefaultSettings): 
    """ """
conf = SuperSettings()
assert conf.p1 == "test_param_name1"  # 在运行时有效,但会产生 p1 不是属性的 linting 错误
英文:

It appears one cannot create an __init__ function on pydantic BaseSettings superclasses.

I am trying to achieve this (note; for demonstration, this code is not supported in pydantic):

class MySettings(BaseSettings):
    
    def __init__(self, foo: str):
        
        self.p1 = f&quot;{foo}_param_name1&quot;  # &lt;- trying to achieve this 
        self.p2 = f&quot;{foo}_param_name2&quot;  # &lt;- trying to achieve this

    redis_host: Optional[str] = &quot;asdf&quot;

settings = MySettings(foo=&quot;test&quot;)
assert settings.redis_host == &quot;asdf&quot;
assert settings.p1 == &quot;test_param_name1&quot; # this will raise a ValueError for p1 not existing
assert settings.p2 == &quot;test_param_name2&quot;

So far, the closest thing I've found is create_model https://docs.pydantic.dev/usage/models/#dynamic-model-creation
but this is not discussed in the context of BaseSettings

Is there a way to achieve a BaseSettings superclass where some values are set based on a parameter?

The following seems pretty nasty and would also greatly confuse code editors:

def retrofit(class_instance: BaseSettings, foo: str):
      setattr(class_instance, &quot;p1&quot;, f&quot;{foo}_param_name1&quot;)

This also works, but causes many linting errors and also confuses editors (seems not to know p1 is a parameter)

def default_settings(foo: str) -&gt; Type[BaseSettings]:
    class DefaultSettings(BaseSettings):
        

        p1 = f&quot;{foo}_param_name1&quot;
        redis_host: Optional[str] = &quot;asdf&quot;


    return DefaultSettings


MyDefaultSettings: Type[BaseSettings] = default_settings(&quot;test&quot;)


class SuperSettings(MyDefaultSettings): # linting error that MyDefaultSettings is not a valid type
    &quot;&quot;&quot; &quot;&quot;&quot;


conf = SuperSettings()
assert conf.p1 == &quot;test_param_name1&quot; # works at runtime, but linting error that p1 is not an attribute

答案1

得分: 1

BaseSettings 有自己的构造函数 __init__,如果你想要重写它,你应该实现与原始构造函数相同的行为 +α。

否则,你可以调用基类(super)的构造函数,它会完成它的工作。正确的继承很重要。

from pydantic import BaseSettings
from typing import Optional


class MySettings(BaseSettings):

    p1: Optional[str]
    p2: Optional[str]
    redis_host: Optional[str] = "asdf"

    def __init__(self, *args, foo: str = None, **kwargs):

        super().__init__(*args, **kwargs)
        if foo is not None:
            self.p1 = f"{foo}_param_name1"  # <- achieved
            self.p2 = f"{foo}_param_name2"  # <- achieved

settings = MySettings(foo="test")
assert settings.redis_host == "asdf"
assert settings.p1 == "test_param_name1" # no ValueError
assert settings.p2 == "test_param_name2"

此代码中没有断言。

英文:

BaseSettings has own constructor __init__ and if you want to override it you should implement same behavior as original constructor +α.

In other case you may call constructor of base (super) class that will do his job. Correct inheritance is matter.

from pydantic import BaseSettings
from typing import Optional


class MySettings(BaseSettings):

    p1: Optional[str]
    p2: Optional[str]
    redis_host: Optional[str] = &quot;asdf&quot;

    def __init__(self, *args, foo: str = None, **kwargs):

        super().__init__(*args, **kwargs)
        if foo is not None:
            self.p1 = f&quot;{foo}_param_name1&quot;  # &lt;- achieved
            self.p2 = f&quot;{foo}_param_name2&quot;  # &lt;- achieved

settings = MySettings(foo=&quot;test&quot;)
assert settings.redis_host == &quot;asdf&quot;
assert settings.p1 == &quot;test_param_name1&quot; # no ValueError
assert settings.p2 == &quot;test_param_name2&quot;

No assertions in this code

huangapple
  • 本文由 发表于 2023年3月15日 19:02:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/75743842.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定