Проверьте, печатается ли поле. Необязательно

Каков наилучший способ проверить, печатается ли поле из класса. Необязательно?

Пример кода:

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, я хочу перечислить все поля Optional. То, что я делаю в данный момент, чтобы решить эту проблему, использует регулярное выражение на основе имени поля, но мне не нравится этот подход. Есть ли лучший способ сделать это?

что такое fields декларация и ожидаемый результат?

RomanPerekhrest 01.07.2019 11:21

Поля извлекают информацию для данного типа без необходимости создания объекта.

Rodrigo Farias Rezino 01.07.2019 11:22

затем опубликовать его. возможно, его можно оптимизировать

RomanPerekhrest 01.07.2019 11:23

Обновлено с дополнительной информацией сейчас

Rodrigo Farias Rezino 01.07.2019 11:25

Обновил пост с рабочим кодом

Rodrigo Farias Rezino 01.07.2019 12:02
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
11
5
6 426
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий

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)))

Это не будет работать на каждой версии python3. В моей библиотеке есть чеки github.com/ltworf/typedload/blob/master/typedload/…github.com/ltworf/typedload/blob/master/typedload/…

LtWorf 01.07.2019 12:34

Примечание: 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__ значение равно type.Union
  4. __args__ должен быть кортеж с двумя элементами, второй из которых является классом NoneType (type(None))

Если все условия оцениваются как истинные, у вас есть typing.Optional[x]

Вам также может понадобиться знать, что является необязательным классом в аннотации:

x = Optional[int].__args__[0]
print(x) # class int

Я написал библиотеку под названием 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 даже между второстепенными выпусками.

Для справки: Python 3.8 (впервые выпущенный в октябре 2019 г.) добавил функции get_origin и get_args в модуль typing.

Примеры из документы:

assert get_origin(Dict[str, int]) is dict
assert get_args(Dict[int, str]) == (int, str)

assert get_origin(Union[int, str]) is Union
assert get_args(Union[int, str]) == (int, str)

Это позволит:

def is_optional(field):
    return typing.get_origin(field) is Union and \
           type(None) in typing.get_args(field)

Вот код совместимости для старых версий Python:

# Python >= 3.8
try:
    from typing import Literal, get_args, get_origin
# Compatibility
except ImportError:
    get_args = lambda t: getattr(t, '__args__', ()) \
                         if t is not Generic else Generic
    get_origin = lambda t: getattr(t, '__origin__', None)

Другой подход (который работает как на python 3.7, так и на 3.8) заключается в том, чтобы понять, как работает операция set Union:

union([x,y],[y])= union([x],[y]) = union(union([x],[y]),[x,y])

Логика в том, что тип Optional не может быть Optionaler. Хотя вы не можете напрямую знать, является ли type обнуляемым/необязательным, Optional[type] будет таким же, как type, является ли type необязательным, а другое (точнее, Union[type,None]) в противном случае.

Итак, в нашем случае:

Union[SomeType,None] == Union[Union[SomeType,None]]

(первый эквивалентен Optional[SomeType], а второй Optional[Optional[SomeType]]

Это позволяет очень легко проверить значения Optional:

from dataclasses import dataclass, fields
from typing import Optional


@dataclass()
class DC:
    x: Optional[str] = None
    y: str = "s"


def get_optional_fields(cls):
    fields_list = fields(cls)
    return [
        field.name 
        for field in fields_list if 
        field.type == Optional[field.type]
    ]



if __name__ == '__main__':
    print(get_optional_fields(DC())) # ['x']

Изящный. Я думаю, что набор текста должен переопределить __eq__ вот так Optional[T] == Union[T, None].

Hannes 29.10.2020 19:16

Другие вопросы по теме