在 post_init 中创建的数据类的属性的访问类型提示
Posted
技术标签:
【中文标题】在 post_init 中创建的数据类的属性的访问类型提示【英文标题】:Access Type Hints for attributes of a dataclass created in post_init 【发布时间】:2021-09-01 08:45:50 【问题描述】:Python:3.7+
我有一个数据类及其子类,如下所示:
from abc import ABC
from dataclasses import dataclass
from typing import Dict, List, Optional
from dbconn import DBConnector
@dataclass
class User:
uid: int
name: str
@dataclass
class Model(ABC):
database: DBConnector
user: User
def func(self, *args, **kwargs):
pass
@dataclass
class Command(Model):
message: Optional[str] = "Hello"
def __post_init__(self):
self.user_id: str = str(self.user.uid)
self.message = f"self.user.name: self.message"
我可以使用typing.get_type_hints(Command)
获得database
、user
和message
的类型提示。
如何获得user_id
的类型提示?
一种解决方法是将 user.uid 和 user.name 作为单独的参数传递给 Command,但当 User 对象具有许多有用的属性时,这并不实用。
我认为它首先不起作用的原因是因为 init 在运行时被调用,这就是类型检查不考虑这些属性的原因。一种可能的解决方案是解析类的 ast,但我不确定这是否推荐和足够通用。如果是,将不胜感激一个工作示例。
【问题讨论】:
为什么不像在User
中那样在__post_init__
之外“声明”它?
@DeepSpace,我觉得这不会是可扩展的。此外,我正在尝试使用这些类型提示创建一些东西 - 一种 ORM。我认为一直为外部模型声明属性并不理想。
【参考方案1】:
通过使用inspect.get_source 和正则表达式匹配Type Hinted 属性想出了一个hacky 解决方案。还必须将数据类转换为最终模型的普通类。
from abc import ABC
from dataclasses import dataclass
import inspect
import re
from typing import Dict, List, Optional
from dbconn import DBConnector
@dataclass
class User:
uid: int
name: str
@dataclass
class Model(ABC):
database: DBConnector
user: User
def func(self, *args, **kwargs):
pass
def get_type_hints(self):
source = inspect.getsource(self.__class__)
# Only need type hinted attributes
patt = r"self\.(?P<name>.+):\s(?P<type>.+)\s="
attrs = re.findall(patt, source)
for attr in attrs:
yield attr + (getattr(self, attr[0]), )
class Command(Model):
message: Optional[str] = "Hello"
def __init__(
self, database: DBConnector,
user: User,
message: Optional[str] = "Hello"
):
super().__init__(database, user)
self.user_id: str = str(self.user.uid)
self.message: Optional[str] = f"self.user.name: self.message"
cmd = Command(DBConnector(), User(123, 'Stack Overflow'))
for attr in cmd.get_type_hints():
print(attr)
# Output
('user_id', 'str', '123')
('message', 'str', 'Stack Overflow: Hello')
如果有人能提出更强大的解决方案,我肯定对此很感兴趣。现在,我会将此标记为我的答案,以防有人偶然发现此问题并且可以使用 hacky 解决方案。
【讨论】:
我无法真正改进这一点,但值得注意的是,在全局命名空间中或作为类变量编译一次正则表达式(例如regex = re.compile(patt)
)可能会更有效,然后在您的Model.get_type_hints
方法中调用regex.findall(source)
。 re.findall(patt, source)
每次调用 Model.get_type_hints
时都必须编译模式。以上是关于在 post_init 中创建的数据类的属性的访问类型提示的主要内容,如果未能解决你的问题,请参考以下文章
通过 phpmyadmin 访问在 mysql 中创建的数据库