Статическая типизация для `__слотов__` в python

#python #slots

Вопрос:

Я знаю, что python использовал утиную типизацию, но мне было интересно, можно ли, в частности, обеспечить проверку типа для переменных класса __slots__ .

например —

 class Student:
def __init__(self, name):
    self.name = name

class Class:
    __slots__ = ('class_representative',
                 'var_2',
                 'var_3',
                 '...',  # Assume many more variables below
                )

    def __init__(self, *args, **kwargs):
        self.class_representative = kwargs.get('cr')
        self.var_2 = kwargs.get('v2')
        self.var_3 = kwargs.get('v3')
        ... # Assume many more variables below
 

В приведенном выше примере, как я могу убедиться, что всякий раз, когда какой-либо объект присваивается class_representative переменной, он всегда должен иметь тип Student ?

Возможно ли что-то вроде следующего?

 class Class:
    __slots__ = ('class_representative': Student,
                 'var_2',
                 'var_3',
                 '...',  # Assume many more variables below
                )
 

Комментарии:

1. Вы должны написать код, чтобы обеспечить это самостоятельно… не уверен, в чем __slots__ заключается актуальность

2. Вы спрашиваете о проверке статической типизации, при которой атрибут получает только Student значения, или о проверке во время выполнения?

3. @Blckknight Я хочу запретить присваивать какие-либо другие типы, кроме Student class_representative переменных, в любой момент выполнения программы.

4. @aaryan: на самом деле это не ответ на мой вопрос. Статическая проверка типов выполняется перед запуском программы и направлена на то, чтобы доказать, что вы никогда не вызывали функцию с неправильным типом. Проверка во время выполнения происходит во время выполнения программы. Они реализованы совершенно по-разному, поэтому вам действительно нужно выбрать, какой из них вы хотите, и сделать это. __slots__ не имеет ничего общего с проверкой типов (любого вида). Его цель состоит в том, чтобы помочь вам сэкономить память для объектов, которые вы собираетесь создавать в огромном количестве, избегая необходимости __dict__ в каждом экземпляре.

5. @Blckknight Извините, что не понял ваш вопрос правильно ранее. Да, я хочу, чтобы здесь была статическая проверка типов. Есть способ, который Silvio Mayolo был дан ниже, но он не работает с синтаксисом *args/**kwargs. Смотрите мой комментарий там для получения более подробной информации.

Ответ №1:

Когда вы говорите «статическая типизация», я предполагаю, что вы имеете в виду PEP 484. В этом случае __slots__ не имеет абсолютно никакого значения, и мы аннотируем тип переменных экземпляра так, как мы всегда делаем для классов Python.

 class Class:
    __slots__ = ('class_representative',)
    class_representative: Student

    def __init__(self, student: Student) -> None:
        self.class_representative = student
 

Кстати, если вы собираетесь проверять статические типы в Python (что я настоятельно рекомендую; это удивительно хорошо продуманная система), прием и пересылка *args и **kwargs в вашем конструкторе-отличный способ потерять любую статическую проверяемость. Возьмите нужные вам аргументы с таким количеством типов, которые вы можете предоставить, и передайте то, что вам нужно.

Комментарии:

1. В некоторых местах у меня есть много аргументов для передачи конструктору. Это будет выглядеть очень некрасиво, если я напишу аргументы так, как вы предложили. Следовательно , я использую **kwargs , но затем я теряю статическую проверяемость. Поэтому мне было интересно, есть ли способ каким-то образом указать их __slots__ .

2. @aaryan: Я не уверен **kwargs , что это делает то, что вы думаете. В вашем примере кода ваш __init__ метод такой же, как если бы его вообще не было. Если вы не реализуете __init__ , вы унаследуете object.__init__ , что не принимает никаких аргументов, кроме как self и ничего не делает. Ваш код принимает произвольные аргументы, но передает их object.__init__ , что вызовет исключение, если вообще есть какие-либо аргументы (любых типов). Ничто в вашем классе не будет настраиваться на class_representative основе каких-либо аргументов в args или kwargs , поэтому я не уверен, что вы хотите ввести проверку.

3. @Blckknight Я улучшил рассматриваемый пример, чтобы сделать его намного проще для понимания проблемы.

4. Я все еще согласен с @Blckknight. Если вы хотите установить переменную экземпляра class_representative в качестве аргумента cr , просто возьмите вызванный аргумент cr и используйте его. **kwargs это для тех случаев, когда вы хотите принять все аргументы ключевых слов в качестве словаря. Это совершенно бессмысленно и стоит вам статической проверяемости, если вы знаете, каких аргументов ожидать.

5. Кроме того, стоит отметить, просто чтобы убедиться, что нет никаких недоразумений. __slots__ это в первую очередь трюк с эффективностью, чтобы уменьшить размер класса. Это ни в коем случае не обязательно для класса в Python. Поэтому, если этот класс не используется в экстремальной ситуации с узким местом (и вы провели сравнительный анализ и обнаружили доказательства этого), то __slots__ , вероятно, преждевременно. В частности, это не требуется в классах, и подавляющее большинство классов Python никогда не указывают его