跳转至

除了可以直接通过字段名(例如 model.foobar )访问模型属性之外,还可以通过多种方式转换、转储、序列化和导出模型。

提示

"序列化与转储" Pydantic 中 "序列化" 和 "转储" 可互换使用。两者都指将模型转换为字典或 JSON 编码的字符串的过程。

Outside of Pydantic, the word "serialize" usually refers to converting in-memory data into a string or bytes. However, in the context of Pydantic, there is a very close relationship between converting an object from a more structured form — such as a Pydantic model, a dataclass, etc. — into a less structured form comprised of Python built-ins such as dict.

While we could (and on occasion, do) distinguish between these scenarios by using the word "dump" when converting to primitives and "serialize" when converting to string, for practical purposes, we frequently use the word "serialize" to refer to both of these situations, even though it does not always imply conversion to a string or bytes.

model.model_dump(...)

API 文档

pydantic.main.BaseModel.model_dump

这是将模型转换为字典的主要方式。子模型将递归地转换为字典。

注意

将子模型转换为字典的一个例外是, RootModel 及其子类将直接转储 root 字段值,而无需包装字典。这也是递归进行的。

注意

您可以使用计算字段将 propertycached_property 数据包含在 model.model_dump(...) 输出中。

示例: 翻译文本:

from typing import Any, List, Optional

from pydantic import BaseModel, Field, Json


class BarModel(BaseModel):
    whatever: int


class FooBarModel(BaseModel):
    banana: Optional[float] = 1.1
    foo: str = Field(serialization_alias='foo_alias')
    bar: BarModel


m = FooBarModel(banana=3.14, foo='hello', bar={'whatever': 123})

# returns a dictionary:
print(m.model_dump())
#> {'banana': 3.14, 'foo': 'hello', 'bar': {'whatever': 123}}
print(m.model_dump(include={'foo', 'bar'}))
#> {'foo': 'hello', 'bar': {'whatever': 123}}
print(m.model_dump(exclude={'foo', 'bar'}))
#> {'banana': 3.14}
print(m.model_dump(by_alias=True))
#> {'banana': 3.14, 'foo_alias': 'hello', 'bar': {'whatever': 123}}
print(
    FooBarModel(foo='hello', bar={'whatever': 123}).model_dump(
        exclude_unset=True
    )
)
#> {'foo': 'hello', 'bar': {'whatever': 123}}
print(
    FooBarModel(banana=1.1, foo='hello', bar={'whatever': 123}).model_dump(
        exclude_defaults=True
    )
)
#> {'foo': 'hello', 'bar': {'whatever': 123}}
print(
    FooBarModel(foo='hello', bar={'whatever': 123}).model_dump(
        exclude_defaults=True
    )
)
#> {'foo': 'hello', 'bar': {'whatever': 123}}
print(
    FooBarModel(banana=None, foo='hello', bar={'whatever': 123}).model_dump(
        exclude_none=True
    )
)
#> {'foo': 'hello', 'bar': {'whatever': 123}}


class Model(BaseModel):
    x: List[Json[Any]]


print(Model(x=['{"a": 1}', '[1, 2]']).model_dump())
#> {'x': [{'a': 1}, [1, 2]]}
print(Model(x=['{"a": 1}', '[1, 2]']).model_dump(round_trip=True))
#> {'x': ['{"a":1}', '[1,2]']}

model.model_dump_json(...)

API 文档

pydantic.main.BaseModel.model_dump_json

.model_dump_json() 方法将模型直接序列化为一个 JSON 编码的字符串,该字符串等同于 .model_dump() 产生的结果。

有关更多信息,请参见 参数

注意

Pydantic 可以将许多常用类型序列化为 JSON,否则这些类型与简单的 json.dumps(foobar) (例如 datetimedateUUID )不兼容。

from datetime import datetime

from pydantic import BaseModel

class BarModel(BaseModel): whatever: int

class FooBarModel(BaseModel): foo: datetime bar: BarModel

m = FooBarModel(foo=datetime(2032, 6, 1, 12, 13, 14), bar={'whatever': 123}) print(m.model_dump_json())

> {"foo":"2032-06-01T12:13:14","bar":{"whatever":123}}

print(m.model_dump_json(indent=2)) """ { "foo": "2032-06-01T12:13:14", "bar": { "whatever": 123 } } """

dict(model) 和迭代

Pydantic 模型也可以使用 dict(model) 转换为字典,并且可以使用 for field_name, field_value in model: 迭代模型的字段。通过这种方法返回原始字段值,因此子模型不会转换为字典。

示例: 翻译文本:

from pydantic import BaseModel


class BarModel(BaseModel):
    whatever: int


class FooBarModel(BaseModel):
    banana: float
    foo: str
    bar: BarModel


m = FooBarModel(banana=3.14, foo='hello', bar={'whatever': 123})

print(dict(m))
#> {'banana': 3.14, 'foo': 'hello', 'bar': BarModel(whatever=123)}
for name, value in m:
    print(f'{name}: {value}')
    #> banana: 3.14
    #> foo: hello
    #> bar: whatever=123

还请注意, RootModel 确实会被转换为一个字典,其中的键为 'root'

自定义序列化器

Pydantic 提供了几个功能序列化器来自定义模型如何序列化为字典或 JSON。

序列化可以使用 @field_serializer 修饰符在字段上进行自定义,也可以使用 @model_serializer 修饰符在模型上进行自定义。

from datetime import datetime, timedelta, timezone
from typing import Any, Dict

from pydantic import BaseModel, ConfigDict, field_serializer, model_serializer


class WithCustomEncoders(BaseModel):
    model_config = ConfigDict(ser_json_timedelta='iso8601')

    dt: datetime
    diff: timedelta

    @field_serializer('dt')
    def serialize_dt(self, dt: datetime, _info):
        return dt.timestamp()


m = WithCustomEncoders(
    dt=datetime(2032, 6, 1, tzinfo=timezone.utc), diff=timedelta(hours=100)
)
print(m.model_dump_json())
#> {"dt":1969660800.0,"diff":"P4DT4H"}


class Model(BaseModel):
    x: str

    @model_serializer
    def ser_model(self) -> Dict[str, Any]:
        return {'x': f'serialized {self.x}'}


print(Model(x='test value').model_dump_json())
#> {"x":"serialized test value"}

注意

通过将特殊值 '*' 传递给 @field_serializer 装饰器,也可以在所有字段上调用单个序列化程序。

此外, PlainSerializer WrapSerializer 使您能够使用函数修改序列化的输出。

这两个序列化器都接受可选参数,包括:

  • return_type 指定函数的返回类型。如果省略,将从类型注释中推断出来。

  • when_used 指定何时使用此序列化程序。接受一个字符串,其值为'always'、'unless-none'、'json'和'json-unless-none'。默认值为'always'。

PlainSerializer 使用一个简单的函数修改序列化的输出。

from typing_extensions import Annotated

from pydantic import BaseModel
from pydantic.functional_serializers import PlainSerializer

FancyInt = Annotated[
    int, PlainSerializer(lambda x: f'{x:,}', return_type=str, when_used='json')
]


class MyModel(BaseModel):
    x: FancyInt


print(MyModel(x=1234).model_dump())
#> {'x': 1234}

print(MyModel(x=1234).model_dump(mode='json'))
#> {'x': '1,234'}

WrapSerializer 接收原始输入以及一个处理函数,该处理函数应用标准序列化逻辑,并可以在返回序列化的最终输出之前修改结果值。

from typing import Any

from typing_extensions import Annotated

from pydantic import BaseModel, SerializerFunctionWrapHandler
from pydantic.functional_serializers import WrapSerializer


def ser_wrap(v: Any, nxt: SerializerFunctionWrapHandler) -> str:
    return f'{nxt(v + 1):,}'


FancyInt = Annotated[int, WrapSerializer(ser_wrap, when_used='json')]


class MyModel(BaseModel):
    x: FancyInt


print(MyModel(x=1234).model_dump())
#> {'x': 1234}

print(MyModel(x=1234).model_dump(mode='json'))
#> {'x': '1,235'}

覆盖转储模型时的返回类型

虽然 .model_dump() 的返回值通常可以描述为 dict[str, Any] ,但是通过使用 @model_serializer ,实际上可以导致它返回一个与该签名不匹配的值:

from pydantic import BaseModel, model_serializer


class Model(BaseModel):
    x: str

    @model_serializer
    def ser_model(self) -> str:
        return self.x


print(Model(x='not a dict').model_dump())
#> not a dict

如果你想这样做,并且仍然希望对此方法进行正确的类型检查,可以在 if TYPE_CHECKING: 块中重写 .model_dump()

from typing import TYPE_CHECKING, Any

from typing_extensions import Literal

from pydantic import BaseModel, model_serializer


class Model(BaseModel):
    x: str

    @model_serializer
    def ser_model(self) -> str:
        return self.x

    if TYPE_CHECKING:
        # Ensure type checkers see the correct return type
        def model_dump(
            self,
            *,
            mode: Literal['json', 'python'] | str = 'python',
            include: Any = None,
            exclude: Any = None,
            by_alias: bool = False,
            exclude_unset: bool = False,
            exclude_defaults: bool = False,
            exclude_none: bool = False,
            round_trip: bool = False,
            warnings: bool = True,
        ) -> str: ...

这个技巧实际上在 RootModel 中被用于这个目的。

序列化子类

标准类型的子类

标准类型的子类会像它们的父类一样自动转储:

from datetime import date, timedelta
from typing import Any, Type

from pydantic_core import core_schema

from pydantic import BaseModel, GetCoreSchemaHandler


class DayThisYear(date):
    """
    Contrived example of a special type of date that
    takes an int and interprets it as a day in the current year
    """

    @classmethod
    def __get_pydantic_core_schema__(
        cls, source: Type[Any], handler: GetCoreSchemaHandler
    ) -> core_schema.CoreSchema:
        return core_schema.no_info_after_validator_function(
            cls.validate,
            core_schema.int_schema(),
            serialization=core_schema.format_ser_schema('%Y-%m-%d'),
        )

    @classmethod
    def validate(cls, v: int):
        return date(2023, 1, 1) + timedelta(days=v)


class FooModel(BaseModel):
    date: DayThisYear


m = FooModel(date=300)
print(m.model_dump_json())
#> {"date":"2023-10-28"}

BaseModelTypedDict 字段的子类实例

当使用其注释本身是结构体类型的字段(例如, BaseModel 子类、数据类等)时,默认行为是将属性值序列化为好像它是注释类型的实例一样,即使它是子类。更具体地说,只有注释类型的字段将包含在转储对象中:

from pydantic import BaseModel


class User(BaseModel):
    name: str


class UserLogin(User):
    password: str


class OuterModel(BaseModel):
    user: User


user = UserLogin(name='pydantic', password='hunter2')

m = OuterModel(user=user)
print(m)
#> user=UserLogin(name='pydantic', password='hunter2')
print(m.model_dump())  # note: the password field is not included
#> {'user': {'name': 'pydantic'}}

警告

"迁移警告" 这种行为与 Pydantic V1 中的工作方式不同,在 Pydantic V1 中,我们总是在递归地将模型转储到字典时包含所有(子类)字段。这种行为改变的动机是为了确保您在序列化时确切知道可以包含哪些字段,即使在实例化对象时传递了子类也是如此。特别是,这有助于防止在添加敏感信息(如秘密)作为子类的字段时出现意外情况。

使用鸭子类型进行序列化 🦆

什么是鸭子类型的序列化?

Duck-typing serialization is the behavior of serializing an object based on the fields present in the object itself,
rather than the fields present in the schema of the object. This means that when an object is serialized, fields present in
a subclass, but not in the original schema, will be included in the serialized output.

This behavior was the default in Pydantic V1, but was changed in V2 to help ensure that you know precisely which
fields would be included when serializing, even if subclasses get passed when instantiating the object. This helps
prevent security risks when serializing subclasses with sensitive information, for example.

如果你想要 v1 风格的鸭子类型序列化行为,可以使用运行时设置,也可以注释个别类型。

  • 字段/类型级别:使用 SerializeAsAny 注释

  • 运行时级别:在调用 model_dump()model_dump_json() 时使用 serialize_as_any 标志

我们将在下面更详细地讨论这些选项:

SerializeAsAny 注释:

如果你想要鸭子类型的序列化行为,可以使用 SerializeAsAny 注释在类型上实现:

from pydantic import BaseModel, SerializeAsAny


class User(BaseModel):
    name: str


class UserLogin(User):
    password: str


class OuterModel(BaseModel):
    as_any: SerializeAsAny[User]
    as_user: User


user = UserLogin(name='pydantic', password='password')

print(OuterModel(as_any=user, as_user=user).model_dump())
"""
{
    'as_any': {'name': 'pydantic', 'password': 'password'},
    'as_user': {'name': 'pydantic'},
}
"""

当一个字段被注解为 SerializeAsAny[<SomeType>] 时,验证行为将与注解为 <SomeType> 时相同,类型检查器如 mypy 也将把该属性视为具有适当的类型。但在序列化时,该字段将被序列化为好像该字段的类型提示是 Any ,这就是这个名字的由来。

serialize_as_any 运行时设置

serialize_as_any 运行时设置可用于序列化模型数据,也可以使用鸭子类型序列化行为。 serialize_as_any 可以作为关键字参数传递给@s 和@s 的 model_dump()model_dump_json 方法。也可以作为关键字参数传递给@s 的 dump_python()dump_json() 方法。

如果将 serialize_as_any 设置为 True ,则模型将使用鸭子类型序列化行为进行序列化,这意味着模型将忽略模式,而是询问对象本身应该如何进行序列化。特别是,这意味着在序列化模型子类时,将包含子类中存在但原始模式中不存在的字段。

如果将 serialize_as_any 设置为 False (这是默认值),则模型将使用该模式进行序列化,这意味着子类中存在但原始模式中不存在的字段将被忽略。

问题

“为什么这面旗帜有用?”有时,您希望确保无论子类中可能添加了哪些字段,序列化的对象都将仅具有原始类型定义中列出的字段。如果您在子类中添加了类似于 password: str 的字段,并且您不想意外地将其包含在序列化输出中,那么这可能会很有用。

例如:

from pydantic import BaseModel


class User(BaseModel):
    name: str


class UserLogin(User):
    password: str


class OuterModel(BaseModel):
    user1: User
    user2: User


user = UserLogin(name='pydantic', password='password')

outer_model = OuterModel(user1=user, user2=user)
print(outer_model.model_dump(serialize_as_any=True))  # (1)!
"""
{
    'user1': {'name': 'pydantic', 'password': 'password'},
    'user2': {'name': 'pydantic', 'password': 'password'},
}
"""

print(outer_model.model_dump(serialize_as_any=False))  # (2)!
#> {'user1': {'name': 'pydantic'}, 'user2': {'name': 'pydantic'}}
  1. 设置 serialize_as_anyTrue 后,结果与 V1 的结果匹配。

  2. serialize_as_any 设置为 False (V2 的默认值)时,子类上存在但父类上不存在的字段不会包含在序列化中。

此设置甚至对嵌套和递归模式也有效。例如:

from typing import List

from pydantic import BaseModel


class User(BaseModel):
    name: str
    friends: List['User']


class UserLogin(User):
    password: str


class OuterModel(BaseModel):
    user: User


user = UserLogin(
    name='samuel',
    password='pydantic-pw',
    friends=[UserLogin(name='sebastian', password='fastapi-pw', friends=[])],
)

print(OuterModel(user=user).model_dump(serialize_as_any=True))  # (1)!
"""
{
    'user': {
        'name': 'samuel',
        'friends': [
            {'name': 'sebastian', 'friends': [], 'password': 'fastapi-pw'}
        ],
        'password': 'pydantic-pw',
    }
}
"""

print(OuterModel(user=user).model_dump(serialize_as_any=False))  # (2)!
"""
{'user': {'name': 'samuel', 'friends': [{'name': 'sebastian', 'friends': []}]}}
"""
  1. 即使嵌套的 User 模型实例也会使用 User 子类特有的字段进行转储。

  2. 即使嵌套的 User 模型实例也会被转储,而不会包含 User 子类特有的字段。

注意

serialize_as_any 运行时标志的行为与 SerializeAsAny 注释的行为几乎相同。有一些细微的差异,我们正在努力解决,但在大多数情况下,您可以期望两者具有相同的行为。有关这些差异的更多信息,请参阅此活跃问题。

覆盖 serialize_as_any 默认值(否)

你可以通过配置一个子类来覆盖 serialize_as_any 的默认设置,该子类覆盖了 BaseModelserialize_as_any 参数的默认设置,然后将其用作任何你希望具有此默认行为的模型的基类(而不是 pydantic.BaseModel )。

例如,如果你想默认使用鸭子类型序列化,可以执行以下操作:

from typing import Any, Dict

from pydantic import BaseModel, SecretStr


class MyBaseModel(BaseModel):
    def model_dump(self, **kwargs) -> Dict[str, Any]:
        return super().model_dump(serialize_as_any=True, **kwargs)

    def model_dump_json(self, **kwargs) -> str:
        return super().model_dump_json(serialize_as_any=True, **kwargs)


class User(MyBaseModel):
    name: str


class UserInfo(User):
    password: SecretStr


class OuterModel(MyBaseModel):
    user: User


u = OuterModel(user=UserInfo(name='John', password='secret_pw'))
print(u.model_dump_json())  # (1)!
#> {"user":{"name":"John","password":"**********"}}
  1. 默认情况下, model_dump_json 将使用鸭子类型的序列化行为,这意味着 password 字段包含在输出中。

pickle.dumps(model)

Pydantic 模型支持高效的序列化和反序列化。

import pickle

from pydantic import BaseModel


class FooBarModel(BaseModel):
    a: str
    b: int


m = FooBarModel(a='hello', b=123)
print(m)
#> a='hello' b=123
data = pickle.dumps(m)
print(data[:20])
#> b'\x80\x04\x95\x95\x00\x00\x00\x00\x00\x00\x00\x8c\x08__main_'
m2 = pickle.loads(data)
print(m2)
#> a='hello' b=123

高级包含和排除

model_dumpmodel_dump_json 方法支持 includeexclude 参数,这些参数可以是集合或字典。这允许嵌套选择要导出的字段:

from pydantic import BaseModel, SecretStr


class User(BaseModel):
    id: int
    username: str
    password: SecretStr


class Transaction(BaseModel):
    id: str
    user: User
    value: int


t = Transaction(
    id='1234567890',
    user=User(id=42, username='JohnDoe', password='hashedpassword'),
    value=9876543210,
)

# using a set:
print(t.model_dump(exclude={'user', 'value'}))
#> {'id': '1234567890'}

# using a dict:
print(t.model_dump(exclude={'user': {'username', 'password'}, 'value': True}))
#> {'id': '1234567890', 'user': {'id': 42}}

print(t.model_dump(include={'id': True, 'user': {'id'}}))
#> {'id': '1234567890', 'user': {'id': 42}}

True 表示我们要排除或包含整个键,就好像我们将其包含在一个集合中一样。这可以在任何深度级别进行。

在从子模型或字典的列表或元组中包含或排除字段时必须特别小心。在这种情况下, model_dump 和相关方法期望用于元素级包含或排除的整数键。要从列表或元组的每个成员中排除一个字段,可以使用字典键 '__all__' ,如下所示:

import datetime
from typing import List

from pydantic import BaseModel, SecretStr


class Country(BaseModel):
    name: str
    phone_code: int


class Address(BaseModel):
    post_code: int
    country: Country


class CardDetails(BaseModel):
    number: SecretStr
    expires: datetime.date


class Hobby(BaseModel):
    name: str
    info: str


class User(BaseModel):
    first_name: str
    second_name: str
    address: Address
    card_details: CardDetails
    hobbies: List[Hobby]


user = User(
    first_name='John',
    second_name='Doe',
    address=Address(
        post_code=123456, country=Country(name='USA', phone_code=1)
    ),
    card_details=CardDetails(
        number='4212934504460000', expires=datetime.date(2020, 5, 1)
    ),
    hobbies=[
        Hobby(name='Programming', info='Writing code and stuff'),
        Hobby(name='Gaming', info='Hell Yeah!!!'),
    ],
)

exclude_keys = {
    'second_name': True,
    'address': {'post_code': True, 'country': {'phone_code'}},
    'card_details': True,
    # You can exclude fields from specific members of a tuple/list by index:
    'hobbies': {-1: {'info'}},
}

include_keys = {
    'first_name': True,
    'address': {'country': {'name'}},
    'hobbies': {0: True, -1: {'name'}},
}

# would be the same as user.model_dump(exclude=exclude_keys) in this case:
print(user.model_dump(include=include_keys))
"""
{
    'first_name': 'John',
    'address': {'country': {'name': 'USA'}},
    'hobbies': [
        {'name': 'Programming', 'info': 'Writing code and stuff'},
        {'name': 'Gaming'},
    ],
}
"""

# To exclude a field from all members of a nested list or tuple, use "__all__":
print(user.model_dump(exclude={'hobbies': {'__all__': {'info'}}}))
"""
{
    'first_name': 'John',
    'second_name': 'Doe',
    'address': {
        'post_code': 123456,
        'country': {'name': 'USA', 'phone_code': 1},
    },
    'card_details': {
        'number': SecretStr('**********'),
        'expires': datetime.date(2020, 5, 1),
    },
    'hobbies': [{'name': 'Programming'}, {'name': 'Gaming'}],
}
"""

对于 model_dump_json 方法也是如此。

模型级和字段级包含和排除

除了显式参数 excludeinclude 传递给 model_dumpmodel_dump_json 方法外,我们还可以将 exclude: bool 参数直接传递给 Field 构造函数:

在字段构造函数( Field(..., exclude=True) )上设置 exclude 优先于 model_dumpmodel_dump_json 上的 exclude / include

from pydantic import BaseModel, Field, SecretStr


class User(BaseModel):
    id: int
    username: str
    password: SecretStr = Field(..., exclude=True)


class Transaction(BaseModel):
    id: str
    value: int = Field(exclude=True)


t = Transaction(
    id='1234567890',
    value=9876543210,
)

print(t.model_dump())
#> {'id': '1234567890'}
print(t.model_dump(include={'id': True, 'value': True}))  # (1)!
#> {'id': '1234567890'}
  1. 由于在 Field 中被排除,因此 value 被排除在输出之外。

也就是说,在字段构造函数( Field(..., exclude=True) )上设置 exclude 并不优先于 model_dumpmodel_dump_json 上的 exclude_unsetexclude_noneexclude_default 参数:

from typing import Optional

from pydantic import BaseModel, Field


class Person(BaseModel):
    name: str
    age: Optional[int] = Field(None, exclude=False)


person = Person(name='Jeremy')

print(person.model_dump())
#> {'name': 'Jeremy', 'age': None}
print(person.model_dump(exclude_none=True))  # (1)!
#> {'name': 'Jeremy'}
print(person.model_dump(exclude_unset=True))  # (2)!
#> {'name': 'Jeremy'}
print(person.model_dump(exclude_defaults=True))  # (3)!
#> {'name': 'Jeremy'}
  1. 由于 exclude_none 设置为 True ,并且 ageNone ,因此 age 被排除在输出之外。

  2. 由于 exclude_unset 设置为 True ,并且在 Person 构造函数中没有设置 age ,因此 age 被排除在输出之外。

  3. 由于 exclude_defaults 设置为 True ,并且 age 采用了 None 的默认值,因此 age 被排除在输出之外。

序列化上下文

你可以将一个上下文对象传递给序列化方法,这些方法可以从装饰后的序列化函数的 info 参数中访问。当你需要在运行时动态更新序列化行为时,这很有用。例如,如果你希望根据动态可控的允许值集转储一个字段,可以通过传递允许的值作为上下文来实现:

from pydantic import BaseModel, SerializationInfo, field_serializer


class Model(BaseModel):
    text: str

    @field_serializer('text')
    def remove_stopwords(self, v: str, info: SerializationInfo):
        context = info.context
        if context:
            stopwords = context.get('stopwords', set())
            v = ' '.join(w for w in v.split() if w.lower() not in stopwords)
        return v


model = Model.model_construct(**{'text': 'This is an example document'})
print(model.model_dump())  # no context
#> {'text': 'This is an example document'}
print(model.model_dump(context={'stopwords': ['this', 'is', 'an']}))
#> {'text': 'example document'}
print(model.model_dump(context={'stopwords': ['document']}))
#> {'text': 'This is an example'}

同样,您也可以使用上下文进行验证。

model_copy(...)

API 文档

pydantic.main.BaseModel.model_copy

model_copy() 允许复制模型(可选更新),这在处理冻结模型时特别有用。

示例: 翻译文本:

from pydantic import BaseModel


class BarModel(BaseModel):
    whatever: int


class FooBarModel(BaseModel):
    banana: float
    foo: str
    bar: BarModel


m = FooBarModel(banana=3.14, foo='hello', bar={'whatever': 123})

print(m.model_copy(update={'banana': 0}))
#> banana=0 foo='hello' bar=BarModel(whatever=123)
print(id(m.bar) == id(m.model_copy().bar))
#> True
# normal copy gives the same object reference for bar
print(id(m.bar) == id(m.model_copy(deep=True).bar))
#> False
# deep copy gives a new object reference for `bar`

本文总阅读量