英文:
Create python class with type annotations programatically
问题
我希望能够像以下这样通过编程方式创建一个Python类:
class Foo(BaseModel):
bar: str = "baz"
以下几乎可以正常工作:
Foo = type("Foo", (BaseModel,), {"bar": "baz"})
但不包括注释,Foo.__annotations__
在第一个示例中设置,但在第二个示例中没有设置。
有没有办法实现这一点?
我的动机是创建一个类装饰器,用于创建一个经过修改类型注释的已装饰类的克隆。注释必须在类创建时设置(而不是事后),以便BaseModel的元类能够看到它们。
<details>
<summary>英文:</summary>
I want to be able to create a python class like the following programmatically:
```python
class Foo(BaseModel):
bar: str = "baz"
The following almost works:
Foo = type("Foo", (BaseModel,), {"bar":"baz})
But doesn't include the annotation, Foo.__annotations__
is set in first example but not the second.
Is there any way to achieve this?
My motivation is to create a class decorator that creates a clone of the decorated class with modified type annotations. The annotations have to be set during class creation (not after the fact) to that the metaclass of BaseModel will see them.
答案1
得分: 4
@Scarlet的评论让我意识到以下解决方案回答了我的问题。
```python
Foo = type(
"Foo",
(BaseModel,),
{
"bar": "baz",
"__annotations__": {"bar": str}
}
)
尽管这解决了我动态调整pydantic类字段类型的激励问题,但需要重写元类的行为,如下所示:
from pydantic.main import ModelMetaclass
class OverrideModelMetaclass(ModelMetaclass):
def __new__(cls, name, bases, attrs):
attrs["__annotations__"] = {
attribute_name: cls.__transform_annotation(annotation)
for attribute_name, annotation in attrs["__annotations__"].items()
}
return super().__new__(cls, name, bases, attrs)
@classmethod
def __transform_annotation(cls, annotation):
# 此方法映射属性注释
return Override[annotation]
class Foo(BaseModel, metaclass=OverrideModelMetaclass):
bar: str
上述代码等效于:
class Foo(BaseModel):
bar: Override[str]
<details>
<summary>英文:</summary>
The comment from @Scarlet made me realise the following solution answers my question.
```python
Foo = type(
"Foo",
(BaseModel,),
{
"bar": "baz,
"__annotations__": {"bar": str}
}
)
Although it turns solving my motivating problem with dynamically tweaking field types on pydantic classes required overriding the behaviour of the metaclass like so:
from pydantic.main import ModelMetaclass
class OverrideModelMetaclass(ModelMetaclass):
def __new__(cls, name, bases, attrs):
attrs["__annotations__"] = {
attribute_name: cls.__transform_annotation(annotation)
for attribute_name, annotation in attrs["__annotations__"].items()
}
return super().__new__(cls, name, bases, attrs)
@classmethod
def __transform_annotation(cls, annotation):
# this method maps over the attribute annotations
return Override[annotation]
class Foo(BaseModel, metaclass=OverrideModelMetaclass):
bar: str
The above is equivalent to:
class Foo(BaseModel):
bar: Override[str]
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论