Проверьте, печатает ли поле. - PullRequest
3 голосов
/ 01 июля 2019

Как лучше всего проверить, печатает ли поле из класса. Опционально?

Пример кода:

from typing import Optional
import re
from dataclasses import dataclass, fields

@dataclass(frozen=True)
class TestClass:
    required_field_1: str
    required_field_2: int
    optional_field: Optional[str]

def get_all_optional_fields(fields) -> list:
    return [field.name for field in fields if __is_optional_field(field)]

def __is_optional_field(field) -> bool:
    regex = '^typing.Union\[.*, NoneType\]$'
    return re.match(regex, str(field.type)) is not None

print(get_all_optional_fields(fields(TestClass)))

Где fields от dataclasses, Iхочу перечислить все поля Optional.Что я делаю в данный момент, чтобы решить эту проблему, так это использование регулярных выражений на основе имени поля, но мне не нравится этот подход.Есть ли лучший способ сделать это?

Ответы [ 3 ]

2 голосов
/ 01 июля 2019

Примечание: typing.Optional[x] является псевдонимом для typing.Union[x, None]

Теперь можно проверить атрибуты аннотации вашего поля ввода, чтобы проверить, определено ли оно как Union [x, None]:Вы можете прочитать его атрибуты __module__, __args__ и __origin__:

from typing import *

def print_meta_info(x):
      print(x.__module__, x.__args__, x.__origin__)

x = Optional[int]
print_meta_info(x) # 'typing', (class Int,), typing.Union

x = Union[int, float]
print_meta_info(x) # 'typing', (class int, class float), typing.Union

x = Iterable[str]
print_meta_info(x) # 'typing', (class int,), typing.Iterable

Вам необходимо выполнить следующие шаги, чтобы определить свою проверку:

  1. Убедитесь, чтоаннотация имеет ключи __module__, __args__ и __origin__
  2. __module__ должны быть установлены на «печатать».Если нет, аннотация не является объектом, определенным модулем набора *
  3. __origin__ значение равно типированию. Union
  4. __args__ должен быть кортежем из 2 элементов, где второйявляется классом NoneType (type(None))

Если все условия оцениваются как true, у вас есть типирование. Опция [x]Вам также может понадобиться узнать, что такое необязательный класс в аннотации:

x = Optional[int].__args__[0]
print(x) # class int
1 голос
/ 01 июля 2019

Optional[X] эквивалентно Union[X, None]. Так что вы могли бы сделать,

import re
from typing import Optional

from dataclasses import dataclass, fields


@dataclass(frozen=True)
class TestClass:
    required_field_1: str
    required_field_2: int
    optional_field: Optional[str]


def get_optional_fields(klass):
    class_fields = fields(klass)
    for field in class_fields:
        if (
            hasattr(field.type, "__args__")
            and len(field.type.__args__) == 2
            and field.type.__args__[-1] is type(None)
        ):
            # Check if exactly two arguments exists and one of them are None type
            yield field.name


print(list(get_optional_fields(TestClass)))
0 голосов
/ 01 июля 2019

Я написал библиотеку с именем typedload, которую можно использовать для этого.

Основное назначение библиотеки - преобразование в / из json и namedtuple / dataclass / attrs, но так как нужно было выполнить эти проверки, он предоставляет функции.

Обратите внимание, что разные версии Python изменяют работу API внутренней типизации, поэтому проверки не будут работать на каждой версии Python.

Моя библиотека обращается к нему внутренне, скрываяподробности для пользователя.

Используя его, код выглядит следующим образом

from typing import *
a = Optional[int]

from typedload import typechecks
typechecks.is_union(a) and type(None) in typechecks.uniontypes(a)

https://github.com/ltworf/typedload

Конечно, если вам не нужно поддерживать несколько Pythonверсии, вы можете не зависеть от библиотеки только для этого, но будущие выпуски могут сломать проверку.Они изменили API даже между второстепенными выпусками.

...