Как программист, долгое время использующий Unity для разработки игр, я использую язык программирования C #. Как вы знаете, C # - это строго типизированный язык, который обычно программируется с использованием объектно-ориентированной парадигмы (конечно, у нее есть и другие парадигмы). Недавно я начал использовать Python для программирования. Прошло 5–6 лет с тех пор, как я в последний раз использовал Python, а в то время я использовал Python 2.7. Поэтому у меня сложилось впечатление, что Python - это процессно-ориентированный язык динамического программирования без типов.

Конечно, теперь я знаю, что ошибаюсь. И я обнаружил, что следующие практики могут помочь таким программистам, как я, использующим C #, также привыкнуть к использованию Python.

Аннотация типа и комментарии к типу

Моей первой попыткой было сделать Python строго типизированным языком, по крайней мере, чтобы он выглядел как строго типизированный язык. Хорошая новость заключается в том, что начиная с Python3.5, Python представил аннотацию типов через PEP 484.

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

def greeting(name: str) -> str:
    return 'Hello ' + name

Как вы можете видеть в приведенном выше примере, аннотация типа выполняется добавлением : <type> после переменной. Кстати, если функция не вернет, тип возврата должен быть None.

def retry(url: Url, retry_count: int) -> None: ...

Но что, если тип более сложный? Например, тип - это список, элементами которого являются int.

Для этой цели можно использовать модуль typing, начиная с Python 3.5. Этот модуль обеспечивает во время выполнения поддержку подсказок типов, как указано в PEP 484, PEP 526, PEP 544, PEP 586 , PEP 589 и PEP 591 . Наиболее фундаментальная поддержка состоит из типов Any, Union, Tuple, Callable, TypeVar и Generic.

С помощью набора текста вы можете добавить аннотацию типа следующим образом:

from typing import List
def scale(scalar: float, vector: List[float]) -> List[float]:
    return [scalar * num for num in vector]

Однако в PEP 484 основное внимание уделялось аннотациям функций, синтаксис для ввода переменных был добавлен в Python 3.6 через PEP 526. Существует два варианта синтаксиса, с выражением инициализатора или без него:

from typing import Optional
foo: Optional[int]  # No initializer
bar: List[str] = []  # Initializer

Другой способ добавить подсказку типа - это комментарий типа, синтаксис аннотации на основе комментариев для кода Python 2. Вот несколько примеров:

x = 1  # type: int
x = 1.0  # type: float
x = True  # type: bool
x = "test"  # type: str
x = u"test"  # type: unicode

Комментарий типа достигается добавлением # type: после переменной.

Интерфейс / абстрактный класс

Модуль стандартной библиотеки Python abc (A bstract B ase C lass) часто используется для определения и проверки интерфейсов. Он определяет метакласс ABCMeta и декораторы @abstractmethod и @abstractproperty.

from abc import ABCMeta, abstractmethod
class AbstractStrategy(metaclass=ABCMeta):    
    @abstractmethod    
    def algorithm_interface(self):        
        pass
class ConcreteStrategyA(AbstractStrategy):    
    def algorithm_interface(self):        
        print("ConcreteStrategyA")

И еще одна альтернатива - использовать библиотеку interface. Это библиотека для объявления интерфейсов и статического подтверждения того, что классы реализуют эти интерфейсы.

from interface import implements, Interface
class MyInterface(Interface):
    def method1(self, x):
        pass
    def method2(self, x, y):
        pass

class MyClass(implements(MyInterface)):
    def method1(self, x):
        return x * 2
    def method2(self, x, y):
        return x + y

Статические методы

Вы можете использовать декоратор @staticmethod для определения статической функции в классе. Статический метод не получает неявный первый аргумент (self). И вот пример:

class C:
    @staticmethod
    def f(arg1, arg2, ...): ...

Теперь статический метод f() можно вызывать либо в классе (например, C.f()), либо в экземпляре (например, C().f()).

Дженерики

Используя упомянутый выше модуль typing, вы можете использовать универсальные шаблоны в Python. Модуль набора текста предоставляет несколько общих коллекций, таких как List, Mapping, Dict. Это общая версия list, Mapping, dict соответственно.

T = TypeVar('T', int, float)
def vec2(x: T, y: T) -> List[T]:
    return [x, y]
def keep_positives(vector: Sequence[T]) -> List[T]:
    return [item for item in vector if item > 0]
def get_position_in_index(word_list: Mapping[str, int], word: str) -> int:
    return word_list[word]
def count_words(text: str) -> Dict[str, int]:
    ...

Пользовательский класс также может быть определен как общий класс. Классу нужно только наследовать Generic. Generic - абстрактный базовый класс для универсальных типов. Например, общий определяемый пользователем класс может быть определен как:

from typing import TypeVar, Generic
from logging import Logger
T = TypeVar('T')
class LoggedVar(Generic[T]):
    def __init__(self, value: T, name: str, logger: Logger) -> None:
        self.name = name
        self.logger = logger
        self.value = value

Класс LoggedVar принимает параметр единственного типа T.

Затем класс LoggedVar можно использовать следующим образом:

T = TypeVar('T')
def foo(logged: LoggedVar[T]) -> None:

Что ж, благодаря вышеуказанным методам программирования я, программист на C #, могу использовать знакомый способ программирования с использованием языка Python. Да!

Спасибо, что прочитали, надеюсь, статья была вам полезна!