1

蟒蛇: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}"

我可以得到类型提示databaseusermessage使用typing.get_type_hints(Command). 如何获得类型提示user_id

一种解决方法是将 user.uid 和 user.name 作为单独的参数传递给 Command,但是当 User 对象具有许多有用的属性时,这并不实用。

我相信它首先不起作用的原因是因为 init 在运行时被调用,这就是类型检查不考虑这些属性的原因。一种可能的解决方案是解析类的 ast,但我不确定这是否推荐和足够通用。如果是,将不胜感激一个工作示例。

4

1 回答 1

0

通过使用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 解决方案。

于 2021-06-17T14:20:28.727 回答