Output of python -c "import pydantic.utils; print(pydantic.utils.version_info())":
pydantic version: 1.7.2
pydantic compiled: True
install path: /Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pydantic
python version: 3.7.6 (v3.7.6:43364a7ae0, Dec 18 2019, 14:18:50) [Clang 6.0 (clang-600.0.57)]
platform: Darwin-18.7.0-x86_64-i386-64bit
optional deps. installed: ['typing-extensions', 'devtools']
from pydantic import BaseModel
class Model(BaseModel):
_foo: str
class Config:
underscore_attrs_are_private = False
m = Model(_foo=1)
print(m._foo)
Traceback (most recent call last):
File "/Users/aovsyannikov/Library/Application Support/JetBrains/PyCharm2020.2/scratches/scratch_138.py", line 10, in <module>
print(m._foo)
AttributeError: 'Model' object has no attribute '_foo'
Hello @AntonOvsyannikov
Well it seems like it's the expected behaviour. Maybe this could/should be changed I have no idea to be honest. @MrMrRobat can probably give more information about it ;)
TL;DR: no, it's not possible, use attr = Field(alias='_attr')
Ignoring underscore attrs was default behavior for a long time, if not always (see pydantic.utils.is_valid_field()).
underscore_attrs_are_private was introduced to allow to use such attrs as private and not just throw them away, but it set to False by default so as not to break existing behavior.
Ok, thanks, clear. But now, been having the Config for such attributes it seems quite artificial limitation, which brings lot of inconvenience when work with models for MongoDB i.g.
@AntonOvsyannikov I quite agree as it makes sense to have fields you don't want to expose but still use them when instantiating. But @MrMrRobat is right when mentioning it is the existing behaviour. Maybe it can be changed in v2.
In the meantime you can use your own BaseModel by overwriting _pydantic_ one to add this behaviour ;)
For example
from typing import Any
from pydantic import BaseModel as PydanticBaseModel
class BaseModel(PydanticBaseModel):
"""Same as default BaseModel but allows private fields in __init__"""
def __init__(__pydantic_self__, **data: Any) -> None:
for name, private_attr in __pydantic_self__.__private_attributes__.items():
if name in data:
# remove private field from data to also work with Config.extra = 'forbid'
private_attr.default = data.pop(name)
super().__init__(**data)
class Model(BaseModel):
_foo: str
class Config:
underscore_attrs_are_private = True
extra = 'forbid'
m = Model(_foo=1)
print(m._foo) # 1
@PrettyWood, it looks like in your example Model. __private_attributes__ defaults will be overwritten on every model init, which seems like unwanted side effect.
@AntonOvsyannikov, just to be sure: your initial problem is about using underscore attributes as regular fields, or about passing private attributes through __init__?
@MrMrRobat
is about using underscore attributes as regular fields
Ok, so seems like problem is really similar to #288.
Ahhh sorry I didn't understand the full problem my bad. Now it all makes sense. Thanks @MrMrRobat for clarifying.
Most helpful comment
@MrMrRobat
is about using underscore attributes as regular fields