Как я могу заставить IDE распознавать желаемое имя типа для статически объявленного, динамически создаваемого класса в Python?

Проблема:

Я работаю над библиотекой, например, с поддержкой типа UInt5, типа Int33 и т. д. Библиотека немного сложнее, чем эта, но для примера можно создать тип UInt12.

def makeUInt(size:int) -> type[UInt]:
   class NewUInt(UInt):
       # Do stuff with size
   NewUInt.__name__ = f"UInt{size}"
   return NewUInt
   
UInt12 = makeUInt(12)
an_example_number = UInt12(508)

Функция IntelliSense в моей IDE (VS Code) распознает тип an_example_number как UInt, а не UInt12.

Проблема:

Я не ожидаю, что динамически объявленные типы будут обнаружены с помощью подсказок типов. Однако я четко указал UInt12 в качестве псевдонима типа, и фактически, если я создаю подкласс вместо псевдонима типа, перейдя

def makeUInt(size:int) -> type[UInt]:
   class NewUInt(UInt):
       # Do stuff with size
   NewUInt.__name__ = f"UInt{size}"
   return NewUInt
   
class UInt12(makeUInt(12)): pass
an_example_number = UInt12(508)

все работает так, как задумано, поэтому очевидно, что на каком-то уровне динамическое объявление можно привести к тому, что понимает IDE.

Например, гипотетически я мог бы сделать так, чтобы UInt вел реестр созданных классов и не позволял UInt12(makeUInt(12)) фактически создавать подклассы. Однако это, очевидно, не идеальный обходной путь.

Спросите:

Как я могу (желательно в Python 3.8) сохранить преимущество динамического создания типов и при этом заставить IDE понимать мою предпочтительную номенклатуру для экземпляров этих типов?

Конечный вариант использования заключается в том, что я хочу явно предоставить определенные типы, не переобъявляя каждый раз # Делать что-то с информацией о размере, чтобы общие типы, такие как UInt16, UInt32 и т. д., могли быть объявлены в моей библиотеке и получать подсказки, тогда как более редкие такие типы, как UInt13, будут объявляться пользователями по мере необходимости и не обязательно получать подсказки.

Задняя часть коробки

def makeUInt(size:int) -> type[UInt]:
   class NewUInt(UInt):
       # Do stuff with size
   NewUInt.__name__ = f"UInt{size}"
   return NewUInt
   
UInt12 = makeUInt(12)
an_example_number = UInt12(508)

Я хотел, чтобы an_example_number отображался как UInt12 с помощью подсказки типа. Он отображается как UInt.

Есть ли у вас return в вашем makeUInt коде?

anatolyg 31.03.2024 12:57

Да, хотя речь явно не об этом. Редактирование соответственно; спасибо за улов @anatolyg

D. Estes McKnight 31.03.2024 13:39

типизация пытается передать «статическую» типизацию динамическому языку, а фабрики классов во время выполнения для произвольных типов настолько динамичны, насколько это возможно. Всегда будут пробелы. Тем не менее, похоже, что можно избежать приведения (typing.cast) каждого возврата makeUnit к уникальному типу.

jsbueno 31.03.2024 20:52

@jsbueno python UInt8 = _UIntStructPackedTypeFactory(8, 'B'); typing.cast(type[UInt8], UInt8); a_uint8 = UInt8(0); Выдает предупреждение «переменная не разрешена в выражении типа» и не сообщает IntelliSense о наведении курсора на то, что a_uint8 является UInt8, а не UInt. Любопытно, python UInt8: TypeAlias = _UIntStructPackedTypeFactory(8, 'B'); typing.cast(type[UInt8], UInt8); a_uint8 = UInt8(0); Выдает «Вызов выражения не разрешен в выражении типа», но анализатор типов это делает см. UInt8 для a_uint8 там.

D. Estes McKnight 01.04.2024 04:29
Почему в 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 может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
4
67
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Как вы уже видели, в вашем случае, вероятно, будет лучше работать явное создание подклассов.

Вместо создания функции фабрики классов определите общее поведение всех производных классов в базовом классе и сделайте так, чтобы его особенности зависели от переменных класса, определенных подклассами.

В примере, похожем на ваш, это может выглядеть так:

class UInt:
    size: int  # to be defined by subclasses

    def __init__(self, value: int):
        if value >= 2 ** self.size:
            raise ValueError("Too big")
        self._value = value

    def __repr__(self):
        return f"{self.__class__.__name__}({self._value})"


class UInt12(UInt):
    size = 12


an_example_number = UInt12(508)
print(an_example_number)  # UInt12(508)

Это была моя первоначальная установка, но у меня 1. в настоящее время есть абстрактные неудовлетворенные методы класса, которые конкретизируются в фабричном процессе (которые зависят от предоставленных значений), и 2. надеялся, что моя реализация будет следовать тому, что, как я ожидал, будут использовать пользователи. -the-fly (объявление типов в DataClasses является довольно заметной особенностью библиотеки, и использование фабрики имеет для нее гораздо больше смысла, чем предыдущее явное объявление класса). Однако в конечном итоге я могу прибегнуть к этому.

D. Estes McKnight 01.04.2024 04:32

Я принял этот ответ. Однако, если кто-нибудь предложит решение, которое сделает свойства, доступные только для чтения, которые необходимо переопределить, более удобными, мне бы хотелось знать!

D. Estes McKnight 04.04.2024 16:57

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