Please complete:
import sys; print(sys.version): 3.7.3import pydantic; print(pydantic.VERSION): 1.2On class construction with deep data structs like arrays and dictionaries, I get a copy. It's not how it works for regular class and unexpected (check class C1 example below). But if I assign array to instance attribute it is just assignment (as expected) and not copy like it is in constructor case.
Could you explain why it works in this way? Or/And help me find the docs that explain it if they exist.
from typing import List, Dict
from pydantic import BaseModel
class C1():
arr = []
def __init__(self, in_arr):
self.arr = in_arr
class C2(BaseModel):
arr: List[int]
arr_orig = [1,9,10,3, 2,3,11,0, 99,30,40,50]
print("id(arr_orig)", id(arr_orig))
c1 = C1(arr_orig)
print("id(c1.arr) ", id(c1.arr))
c2 = C2(arr=arr_orig)
print("id(c2.arr) ", id(c2.arr))
c2.arr = arr_orig
print("id(c2.arr) ", id(c2.arr))
...
validation has to iterate over every element of the list to check each item is valid as an int input and perhaps coerce values to ints. e.g. ['1', 2.1, b'3'] will be coerced to [1, 2, 3]. This requires creating an new list, hence the new id, that's unavoidable with pydantic.
you don't have validation_assignment=True set on the config of C2, thus assignment isn't run and standard attribute assignment keeps the list. If you change the code too
...
class C2(BaseModel):
arr: List[int]
class Config:
validate_assignment = True
...
You'll see the id does change.
If you see any parts of the docs that need improving to clarify this, feel free to submit a PR.
It would be really nice to stress in docs what arguments passed to constructor of pydantic based class would be copied (because of validation, etc).
Also here in docs https://pydantic-docs.helpmanual.io/usage/dataclasses/:
«Keep in mind that pydantic.dataclasses.dataclass is a drop-in replacement for dataclasses.dataclass with validation» It' also misleading if you don't understand that validation is also means of copying data.
And «You can use all the standard pydantic field types, and the resulting dataclass will be identical to the one created by the standard library dataclass decorator.» – it's just simple untrue. It's not identical, because pydantic's dataclass is also copies data.
agreed, PR welcome.
Just for my understanding, when the attribute is a nested model and I pass an instance of the nested model, is it also unavoidable to create a copy?
Example:
from pydantic import BaseModel
class Child(BaseModel):
length: int
class Parent(BaseModel):
child: Child
if __name__ == "__main__":
c = Child(length=5)
p = Parent(child=c)
print("Original child:", id(c))
print("Parent's child:", id(p.child))