Какая типизация реализована в python
Перейти к содержимому

Какая типизация реализована в python

  • автор:

Python: Сильная (или Строгая) типизация

Python — один из языков, который строго относится к типам данных. Поэтому на любую несовместимость типов он ответит ошибкой. Все дело в сильной типизации.

Нам известно про два разных типа данных: числа и строки. Например, мы могли складывать числа, потому что операция сложения — это операция для типа «числа». А что, если применить эту операцию не к двум числам, а к числу и строке?

print(1 + '7') # TypeError: unsupported operand type(s). 

Python не разрешит сложить число 1 и строку ‘7’ , потому что это значения разных типов. Нужно сначала либо сделать строку числом, либо число строкой. Как это сделать, мы поговорим позже.

Такое педантичное отношение к совместимости типов называется строгой типизацией или сильной типизацией. Python — язык со строгой типизацией.

Не все языки так делают. Например, PHP — это язык со слабой типизацией. Он знает о существовании разных типов, но относится к их использованию не очень строго. PHP пытается преобразовывать информацию, когда это кажется разумным. То же самое относится к JavaScript:

// Как тебе такое, Илон Маск? // Число 1 + Строка 7 = Строка 17 1 + '7'; // '17' 

С одной стороны, автоматическое неявное преобразование типов и правда кажется удобным. Но на практике это свойство языка создает множество ошибок и проблем, которые трудно найти. Код может иногда работать, а иногда не работать — в зависимости от того, «повезло» ли с автоматическим преобразованием. Программист это заметит не сразу и потратит много времени на отладку.

Задание

Выведите на экран результат выражения: 7 — (-8 — -2) . Попробуйте сделать число 7 не числом, а строкой. Сработает ли такой код? Поэкспериментируйте с другими числами тоже.

Упражнение не проходит проверку — что делать? ��

Если вы зашли в тупик, то самое время задать вопрос в «Обсуждениях». Как правильно задать вопрос:

  • Обязательно приложите вывод тестов, без него практически невозможно понять что не так, даже если вы покажете свой код. Программисты плохо исполняют код в голове, но по полученной ошибке почти всегда понятно, куда смотреть.

В моей среде код работает, а здесь нет ��

Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.

Мой код отличается от решения учителя ��

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

В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.

Прочитал урок — ничего не понятно ��

Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в «Обсуждениях». Идеально, если вы сформулируете непонятные моменты в виде вопросов. Обычно нам нужно несколько дней для внесения правок.

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

Протоколы в Python: утиная типизация по-новому

В новых версиях Python аннотации типов получают всё большую поддержку, всё чаще и чаще используются в библиотеках, фреймворках, и проектах на Python. Помимо дополнительной документированности кода, аннотации типов позволяют таким инструментам, как mypy, статически произвести дополнительные проверки корректности программы и выявить возможные ошибки в коде. В этой статье пойдет речь об одной, как мне кажется, интересной теме, касающейся статической проверки типов в Python – протоколах, или как сказано в PEP-544, статической утиной типизации.

Утиная типизация

Часто, когда речь заходит о Python, всплывает фраза утиная типизация, или даже что-нибудь вроде:

Если это выглядит как утка, плавает как утка и крякает как утка, то это, вероятно, и есть утка

Утиная типизация – это концепция, характерная для языков программирования с динамической типизацией, согласно которой конкретный тип или класс объекта не важен, а важны лишь свойства и методы, которыми этот объект обладает. Другими словами, при работе с объектом его тип не проверяется, вместо этого проверяются свойства и методы этого объекта. Такой подход добавляет гибкости коду, позволяет полиморфно работать с объектами, которые никак не связаны друг с другом и могут быть объектами разных классов. Единственное условие, чтобы все эти объекты поддерживали необходимый набор свойств и методов.

>>> class Meter: . def __len__(self): . return 1_000 . >>> len([1, 2, 3]) 3 >>> len("Duck typing. ") 14 >>> len(Meter()) 1000

В примере выше функции len не важен тип аргумента, а важно лишь то, что у объекта можно вызвать метод __len__() .

Но именно эта гибкость и усложняет раннее обнаружение ошибок типизации. Корректность использования объектов определяется динамически, в момент выполнения программы, и зачастую тестирование – единственный способ отловить подобные ошибки. Статическая проверка типов и корректности программы в данном случае представляет значительную сложность.

Номинальная типизация

При номинальной типизации (nominal type system) совместимость типов определяется, основываясь на явных декларациях в коде программы, например, на именах классов и иерархии наследования. Если класс Duck явно объявлен наследником класса Bird , то объекты класса Duck могут быть использованы везде, где ожидаются объекты класса Bird . Применительно к Python, mypy может статически, без непосредственного запуска программы, основываясь только на исходном коде, проверить такую совместимость.

Рассмотрим небольшой пример:

class Bird: def feed(self) -> None: print("Feeding the bird. ") class Duck(Bird): def feed(self) -> None: print("Feeding the duck. ") class Goose: """ Этот класс по каким-то причинам не объявлен наследником класса Bird. """ def feed(self) -> None: print("Feeding the goose. ") def feed(bird: Bird) -> None: bird.feed() # OK feed(Bird()) # OK feed(Duck()) # Mypy error: Argument 1 to "feed" has incompatible type "Goose"; # expected "Bird" feed(Goose()) # Mypy error: Argument 1 to "feed" has incompatible type "None"; # expected "Bird" feed(None)

Хотя класс Goose имеет нужный нам метод feed , с точки зрения номинальной типизации он не является подтипом Bird , о чем и сообщает mypy.

Проверка совместимости типов в соответствии с номинальной типизацией и иерархией наследования существует во многих языках программирования. Например, Java, C#, C++ и многие другие языки используют номинальную систему типов.

Структурная типизация

Структурная типизация (structural type system) определяет совместимость типов на основе структуры этих типов, а не на явных декларациях. Подобный механизм может рассматриваться как некоторый аналог утиной типизации, но для статических проверок, в некотором смысле compile time duck typing.

Структурная типизация также довольно широко распространена. Например, интерфейсы в Go – это набор методов, которые определяют некоторую функциональность. Типы, реализующие интерфейсы в Go не обязаны декларировать каким-либо образом, что они реализуют данный интерфейс, достаточно просто реализовать соответствующие методы интерфейса.

Другой пример – это TypeScript, который также использует структурную систему типов:

// TypeScript interface Person < name: String age: Number >function show(person: Person) < console.log("Name: " + person.name) console.log("Age: " + person.age) >class Employee < name: String age: Number constructor(name: String, age: Number) < this.name = name this.age = age >> class Figure <> // OK show(new Employee("John", 30)) // OK show() // Error: // Argument of type 'Figure' is not assignable to parameter of type 'Person'. // Type 'Figure' is missing the following properties // from type 'Person': name, age show(new Figure())

Здесь класс Employee является подтипом Person , хотя в коде нет никаких явных деклараций наследования. Важно лишь то, что Employee имеет необходимые свойства name и age . Класс Figure , напротив, не имеет указанных свойств и, следовательно, не может быть использован там, где ожидается Person .

Python и протоколы

Начиная с версии Python 3.8 (PEP-544), появляется новый механизм протоколов для реализации структурной типизации в Python. Термин протоколы давно существует в мире Python и хорошо знаком всем, кто работает с языком. Можно вспомнить, например, протокол итераторов, протокол дескрипторов, и несколько других.

Новые протоколы в некотором смысле «перегружают» уже устоявшийся термин, добавляя возможность структурно проверять совместимость типов при статических проверках (с помощью, например, mypy). В момент исполнения программы, протоколы в большинстве случаев не имеют какого-то специального значения, являются обычными абстрактными классами ( abc.ABC ), и не предназначены для инстанциирования объектов напрямую.

Рассмотрим следующий пример:

import typing as t # t.Iterable[int] - это протокол итераций def iterate_by(numbers: t.Iterable[int]) -> None: for number in numbers: print(number) # OK iterate_by([1, 2, 3]) # OK iterate_by(range(1_000_000)) # Mypy error: Argument 1 to "iterate_by" has incompatible type "str"; # expected "Iterable[int]" # note: Following member(s) of "str" have conflicts: # note: Expected: # note: def __iter__(self) -> Iterator[int] # note: Got: # note: def __iter__(self) -> Iterator[str] iterate_by("duck")

Mypy сообщит нам об ошибке, если переданный в функцию iterate_by объект не будет поддерживать протокол итераций (напомню, у объекта должен быть метод __iter__ возвращающий итератор).

Если мы объявим собственный класс, который будет поддерживать протокол итераций, то mypy сможет точно так же, статически проверить соответствие объектов нашего класса заявленному протоколу.

# . продолжение предыдущего примера class Fibonacci: def __iter__(self) -> t.Iterator[int]: a, b = 0, 1 while True: yield a a, b = b, a + b # OK iterate_by(Fibonacci()) class Animals: """ Этот класс, хотя и достаточно интересен сам по себе, но не поддерживает итерации по целым числам, поэтому не соответствует нашему протоколу. """ def __iter__(self) -> t.Iterator[str]: yield from ["duck", "cat", "dog"] # Mypy error: Argument 1 to "iterate_by" has incompatible type "Animals"; # expected "Iterable[int]" iterate_by(Animals())

В стандартной библиотеке (в модуле typing ) определено довольно много протоколов для статических проверок. Полный список и примеры использования встроенных протоколов можно посмотреть в документации mypy.

Пользовательские протоколы

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

Пример использования

Разберем небольшой пример использования пользовательских протоколов:

import typing as t class Figure(t.Protocol): """Геометрическая фигура.""" # Протоколы могут определять не только методы, но и атрибуты name: str def calculate_area(self) -> float: """Вычислить площадь фигуры.""" def calculate_perimeter(self) -> float: """Вычислить периметр фигуры.""" def show(figure: Figure) -> None: print(f"S () = ") print(f"P () = ")

Класс декларирующий протокол должен являться наследником класса Protocol определенного в модуле typing . Атрибуты и методы перечисленные в теле класса-протокола должны быть реализованы во всех классах, соответствующих данному протоколу. В общем случае тело методов класса-протокола не имеет значения (хотя и существует возможность добавить реализацию методов по-умолчанию).

Объявим несколько классов, соответствующих протоколу Figure .

# . продолжение предыдущего примера class Square: name = "квадрат" def __init__(self, size: float): self.size = size def calculate_area(self) -> float: return self.size * self.size def calculate_perimeter(self) -> float: return 4 * self.size def set_color(self, color: str) -> None: """ Класс может содержать собственные методы, которые не относятся к протоколу. """ self.color = color # OK show(Square(size=3.14))

Обратите внимание, что класс Square номинально не является наследником класса Figure . Mypy может проверить соответствие аргумента функции show протоколу Figure , основываясь на структуре класса Square . В этом смысле, структурная типизация позволяет сократить внутренние зависимости между частями кода. Представим, что протокол Figure и функция show объявлены в одном модуле, а класс Square – в совершенно другом (или даже эти классы находятся в разных библиотеках). При этом между двумя модулями не будет никаких зависимостей, что может способствовать более гибкому проектированию приложения.

Если реализация протокола будет некорректной, то mypy сообщит об ошибке:

# . продолжение предыдущего примера class Circle: PI = 3.1415926 name = "окружность" def __init__(self, radius: float): self.radius = radius def calculate_perimeter(self) -> float: return 2 * self.PI * self.radius # Mypy error: Argument 1 to "show" has incompatible type "Circle"; # expected "Figure" # note: 'Circle' is missing following 'Figure' protocol member: # note: calculate_area show(Circle(radius=1))

В данном примере mypy не только сообщает об ошибке в коде программы, но и подсказывает какой метод протокола не реализован (или реализован неправильно).

Явная имплементация протокола

Помимо неявной имплементации, разобранной в примерах выше, есть возможность явно имплементировать протокол. В таком случае mypy сможет проверить, что все методы и свойства протокола реализованы правильно.

import typing as t import abc class Readable(t.Protocol): @abc.abstractmethod def read(self) -> str: . def get_size(self) -> int: """ Этот метод имеет реализацию по-умолчанию. """ return 1_000 # OK class File(Readable): def read(self) -> str: return "содержимое файла" # OK print(File().get_size()) # Выведет 1000 # Mypy error: Return type "int" of "read" incompatible # with return type "str" in supertype "Readable" class WrongFile(Readable): def read(self) -> int: return 42

В случае явной имплементации протоколы становятся больше похожи на абстрактные классы ( abc.ABC ), позволяют проверять корректность реализации методов и свойств, а так же использовать реализацию по-умолчанию. Но опять же, явное наследование не является обязательным, соответствие произвольного объекта протоколу mypy сможет проверить при статическом анализе.

Декоратор runtime_checkable

В основном протоколы не предназначены для проверок времени выполнения, наподобие isinstance и issubclass . Например, если в коде будет следующая проверка, то mypy сообщит об ошибке:

# . продолжение предыдущего примера с протоколом Figure s = Square(4) # Mypy error: Only @runtime_checkable protocols can be used # with instance and class checks # isinstance(s, Figure) isinstance(s, Figure)

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

import typing as t @t.runtime_checkable class HasLength(t.Protocol): def __len__(self) -> int: . # OK print(isinstance("Утка", HasLength)) # Выведет True print(isinstance([1, 2, 3], HasLength)) # Выведет True

Хотя это и может быть полезно в каких-то случаях, у этого метода есть несколько серьезных ограничений, которые подробно разобраны в PEP-544.

Несколько слов в заключение

Новые протоколы являются продолжением идеи утиной типизации в современном Python, в котором инструменты статической проверки типов заняли довольно важное место. Mypy и другие подобные инструменты теперь имеют возможность использовать структурную типизацию в дополнение к номинальной для проверки корректности кода на Python. Кроме того, если вы используете аннотации и проверки типов в своих проектах, то новые протоколы могут сделать код более гибким, сохраняя при этом безопасность и уверенность, которую дает статическая типизация.

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

Примечания

  • Все примеры рассмотренные в статье проверялись в Python 3.9 / mypy 0.812.
  • Файл настроек mypy

Полезные ссылки

  • mypy
  • PEP-544 – Protocols: Structural subtyping
  • Утиная типизация (Глоссарий Python)
  • Nominal type system (Wikipedia)
  • Structural type system (Wikipedia)
  • The Iterator Protocol: How «For Loops» Work in Python
  • Protocols and structural subtyping (Документация mypy)
  • Интерфейсы в Go

Типизация в Python

Очень часто языки программирования сравнивают по их типизации. Иначе говоря — по тому, как устроена их система типов. Давайте разберемся, какая типизация у Python, и что это означает.

Типизация в Python

Для начала мы можем смело сказать, что типизация в Python существует, а следовательно, он относится к типизированным языкам. У многих низкоуровневых языков (вроде ассемблера) вообще нет типизации — любые структуры в них — не более чем набор битов. Типизация позволяет упростить процесс обработки информации. Если данные имеют тип, то машина будет взаимодействовать с ними по правилам, установленным для этого типа.

Неявная типизация

Неявная типизация подразумевает возможность создавать объекты, не указывая их тип.

a = 1 # int b = 1.1 # float c = 'a' # str 

Если бы в Python была явная типизация, приходилось бы каждый раз указывать тип любой переменной. Но и у нее есть свои плюсы. Например, иногда полезно указывать, данные каких типов принимает функция, метод или аргумент. Впрочем, Python позволяет и такое:

def func(a: int, b: str) -> float: return round(float(a / len(b)), 2) var: float = func(3, [1, 1, 1]) # 1.0 # Expected type 'str', got 'list[int]' instead 

Это называется аннотацией типов. Ее возможности сильно расширяет модуль typing , активно развивающийся с версии Python 3.5. Аннотации никак не влияют на выполнение программы, но IDE может считывать их и предупреждать, если вы использовали не тот тип.

Сильная типизация

Python — язык с сильной типизацией. Это означает, что различные типы нельзя смешивать в одних выражениях.

2 + '2' # Traceback (most recent call last): # File "C:\main.py", line 1, in # 2 + '2' # ~~^~~~~ # TypeError: unsupported operand type(s) for +: 'int' and 'str' 

Если бы Python был языком со слабой типизацией, результатом выполнения такого кода стало бы 22 . В ситуации, когда смешиваются разные типы, слабо типизированные языки могут неявно приводить значения к одному из них. Иногда это вызывает непредсказуемые последствия, например, если по неосторожности использовать строку с цифрами вместо числа. Однако Python допускает подобное в некоторых случаях:

2 + 2.2 # 4.2 

Типы int и float могут свободно взаимодействовать. Это продиктовано удобством и естественностью таких преобразований.

Сами понятия «сильной» и «слабой» типизации довольно размыты и зависят от множества конкретных решений при разработке языка. Правильнее будет говорить, что какие-то языки более сильные, чем другие.

Динамическая типизация

Python — язык с динамической типизацией. Это означает, что с определенным типом связывается не переменная, а ее значение. Если бы Python был языком со статической типизацией, мы бы не смогли сделать так:

a = 1 a = 'a' a = SomeClass() 

Динамическая типизация — одна из причин популярности Python. Для начала, это просто удобно. Программа может менять типы переменных на лету, пользуясь их особенностями.

a = (1, 1, 1, 3, 1, 1) a = list(set(a)) # [1, 3] 

Не менее важно то, что динамическая типизация позволяет максимально естественно абстрагироваться от типов и заниматься обобщенным программированием.

def second(a): try: return a[1] except TypeError: return None second([1, 2]) # 2 second('abcd') # b second(1) # None 

Однако за такое удобство приходится платить. Динамическая типизация, вместе с интерпретацией кода, стала причиной главнейшего проклятия Python — низкой скорости работы. К тому же, в языках со статической типизацией есть свои механизмы обобщенного программирования, использующие шаблоны или дженерики.

Утиная типизация

«If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck.»

В Python применяется утиная типизация. Это означает, что тип данных не имеет значения — важно лишь то, какие методы и свойства они поддерживают. Например, чтобы узнать, длину объекта, мы можем использовать функцию len() . Она не проверяет, к какому типу относится объект, а всего лишь обращается к магическому методу __len__() . Можно узнать длину любого объекта, у которого он прописан (не важно, как именно). И наоборот, объект с очевидной длиной, но без метода __len__() нельзя обработать этой функцией.

class SomeClass: length = 12 def __len__(self): return self.length len('123') # 3 len([1, 2]) # 2 len(SomeClass()) # 12 len(123) # Traceback (most recent call last): # File "C:\main.py", line 13, in # len(123) # TypeError: object of type 'int' has no len() 
Заключение

Каждый из вариантов типизации имеет свои преимущества и недостатки. Создатели языков программирования выбирали их комбинации, исходя из своих целей. Гвидо Ван Россум хотел сделать Python максимально удобным и понятным. Благодаря неявной, динамической и утиной типизации, программы на Python выходят лаконичными и простыми для понимания. В то же время, Python имеет строгую типизацию, почти не допускающую неявных преобразований.

Практический Python для начинающих

Практический Python для начинающих

Станьте junior Python программистом за 7 месяцев

Типизация языков программирования: разбираемся в основах

Аватарка пользователя Мария Кривоченко

Типизация языков программирования‌ ‌определяет,‌ ‌как‌ ‌вы‌ ‌будете‌ ‌работать‌ ‌с‌ ‌типами‌ ‌переменных. Рассказываем о ней подробнее.

Типизация языков программирования ‌—‌ ‌это‌ ‌то,‌ ‌как‌ ‌различные‌ ‌языки‌ ‌распознают‌ ‌типы‌ ‌переменных.‌ ‌Она‌ ‌определяет,‌ ‌как‌ ‌вы‌ ‌будете‌ ‌работать‌ ‌с‌ ‌типами‌ ‌переменных:‌ ‌нужно‌ ‌ли‌ ‌их‌ ‌задавать‌ ‌изначально,‌ ‌можно‌ ‌ли‌ ‌изменять‌ ‌и‌ ‌так‌ ‌далее.‌ ‌

Виды‌ ‌типизации‌

Языки‌ ‌программирования‌ ‌бывают‌ ‌типизированными‌ ‌и‌ ‌нетипизированными‌ ‌(бестиповыми).‌ ‌

Бестиповая‌ ‌типизация‌ ‌в‌ ‌основном‌ ‌присуща‌ ‌старым‌ ‌и‌ ‌низкоуровневым‌ ‌языкам‌ ‌программирования,‌ ‌например‌ ‌‌Forth.‌ ‌‌Все‌ ‌данные‌ ‌в‌ ‌таких‌ ‌языках‌ ‌считаются‌ ‌цепочками‌ ‌бит‌ ‌произвольной‌ ‌длины‌ ‌и,‌ ‌как‌ ‌следует‌ ‌из‌ ‌названия,‌ ‌не‌ ‌делятся‌ ‌на‌ ‌типы.‌ ‌Работа‌ ‌с‌ ‌ними‌ ‌труднее,‌ ‌и‌ ‌при‌ ‌чтении‌ ‌кода‌ ‌не‌ ‌всегда‌ ‌ясно,‌ ‌о‌ ‌каком‌ ‌типе‌ ‌переменной‌ ‌идет‌ ‌речь.‌ ‌Это‌ ‌можно‌ ‌исправить,‌ ‌написав‌ ‌комментарии‌ ‌к‌ ‌коду.‌

При‌ ‌этом‌ ‌у‌ ‌нетипизированных‌ ‌языков‌ ‌немало‌ ‌плюсов.‌ ‌В‌ ‌них‌ ‌можно‌ ‌совершать‌ ‌операции‌ ‌с‌ ‌любыми‌ ‌данными,‌ ‌и‌ ‌код‌ ‌получается‌ ‌более‌ ‌эффективным.‌ ‌

Перейдём‌ ‌к‌ ‌типизированным языкам.‌ ‌

Статическая‌ ‌и‌ ‌динамическая‌ ‌типизация‌ ‌

Особенность‌ ‌языков‌ ‌программирования‌ ‌со‌ ‌статической‌ ‌типизацией‌ ‌в‌ ‌том,‌ ‌что‌ ‌проверка‌ типов‌ ‌начинается‌ ‌на‌ ‌стадии‌ ‌компиляции.‌ ‌Компиляторы‌ ‌ищут‌ ‌ошибки‌ ‌ещё‌ ‌до‌ ‌запуска‌ программы,‌ ‌и‌ ‌вам‌ ‌не‌ ‌нужно‌ ‌раз‌ ‌за‌ ‌разом‌ ‌запускать‌ ‌её,‌ ‌чтобы‌ выяснить,‌ ‌что‌ ‌пошло‌ ‌не‌ ‌так.‌ ‌Благодаря‌ ‌этому‌ ‌статически‌ ‌типизированные языки программирования ‌зачастую‌ ‌быстрее.‌ ‌Кроме‌ ‌того,‌ ‌тип‌ ‌для‌ ‌переменной‌ ‌можно‌ ‌назначить‌ ‌только‌ ‌один‌ ‌раз.‌ ‌Например‌ ‌‌в‌ ‌Java‌ ‌такая‌ ‌запись‌ ‌вызовет‌ ‌ошибку‌ ‌на‌ ‌этапе‌ ‌компиляции:‌ ‌

int‌ ‌data;‌ ‌ data‌ ‌=‌ ‌20;‌ ‌ data‌ ‌=‌ ‌"Hello";‌ 

В‌ ‌свою‌ ‌очередь,‌ ‌языки‌ ‌с‌ ‌динамической‌ ‌типизацией‌ ‌ищут‌ ‌ошибки‌ ‌на‌ ‌стадии‌ ‌исполнения.‌ ‌В‌ ‌них‌ ‌можно‌ ‌задать‌ ‌разные‌ ‌типы‌ ‌для‌ ‌одной‌ ‌и‌ ‌той‌ ‌же‌ ‌переменной,‌ ‌и‌ ‌они‌ ‌более‌ ‌гибкие.‌ ‌Например, в‌ ‌Python‌ ‌возможна‌ ‌такая‌ ‌запись,‌ ‌и‌ ‌ошибки‌ ‌не‌ ‌будет:‌ ‌

data‌ ‌=‌ ‌8‌ data‌ ‌=‌ ‌"Hello"‌ 

Сильная‌ ‌и‌ ‌слабая‌ ‌типизация‌ ‌

В‌ ‌слабо‌ ‌типизированных‌ ‌языках‌ ‌программирования‌ ‌можно‌ ‌смешивать‌ ‌разные‌ ‌типы‌ ‌данных. Так‌ ‌код‌ ‌получается‌ ‌короче‌ ‌—‌ ‌язык‌ ‌«старается‌»‌ ‌сам‌ ‌выполнять‌ ‌операции‌ ‌преобразования‌ ‌с‌ ‌разными‌ ‌типами.‌ ‌Впрочем,‌ ‌в‌ ‌таком‌ ‌случае‌ ‌не‌ ‌всегда‌ ‌ясно,‌ ‌как‌ ‌поведёт‌ ‌себя‌ ‌программа.‌ ‌Например,‌ ‌в‌ ‌JavaScript‌ ‌возможна‌ ‌такая‌ ‌запись:‌ ‌

5‌‌ ‌‌+‌‌ ‌‌'6'‌;‌ ‌‌//‌ ‌'56'‌ ‌ 4‌‌ ‌‌*‌‌ ‌‌'3'‌;‌ ‌‌//‌ ‌12‌ ‌ 2‌‌ ‌‌+‌‌ ‌‌true‌;‌‌//‌ ‌3‌ ‌ false‌‌ ‌-‌ ‌‌4‌;‌‌//‌ ‌-4‌ 

При‌ ‌сильной‌ ‌или‌ ‌строгой‌ ‌типизации,‌ ‌как‌ ‌в‌ ‌Python,‌ ‌язык‌ ‌не‌ ‌позволяет‌ ‌смешивать‌ ‌разные‌ ‌типы‌ ‌—‌ ‌то‌ ‌есть,‌ ‌если‌ ‌вы‌ ‌обозначили‌ ‌переменную‌ ‌как‌ ‌число,‌ ‌то‌ ‌добавить‌ ‌к‌ ‌ней‌ ‌строку‌ ‌уже‌ ‌не‌ ‌получится:‌ ‌ ‌

‌foo‌ ‌=‌ ‌"x"‌ ‌ ‌ foo‌ ‌=‌ ‌foo‌ ‌+‌ ‌5‌ ‌ ‌ Traceback‌ ‌(most‌ ‌recent‌ ‌call‌ ‌last):‌ ‌ ‌ ‌File‌ ‌"",‌ ‌line‌ ‌1,‌ ‌in‌ ‌?‌ ‌ ‌ ‌foo‌ ‌=‌ ‌foo‌ ‌+‌ ‌5‌ ‌ TypeError:‌ ‌cannot‌ ‌concatenate‌ ‌'str'‌ ‌and‌ ‌'int'‌ ‌objects‌ ‌ ‌ 

Языки‌ ‌с‌ ‌сильной‌ ‌типизацией‌ ‌надёжнее.‌ ‌Да и‌ ‌программист,‌ ‌прописывая‌ ‌все‌ ‌преобразования‌ ‌вручную,‌ ‌лучше‌ ‌понимает,‌ ‌как‌ ‌работает‌ ‌его‌ ‌код.‌ ‌

Явная‌ ‌и‌ ‌неявная‌ ‌типизация‌ ‌

В‌ ‌языках‌ ‌программирования‌ ‌с‌ ‌явной‌ ‌типизацией‌ ‌типы‌ ‌переменных‌ ‌и‌ ‌возвращаемых‌ ‌значений‌ ‌функций‌ ‌нужно‌ ‌задавать.‌ ‌Это‌ ‌дольше,‌ ‌но‌ ‌так‌ ‌проще‌ ‌определять,‌ ‌что‌ ‌значат‌ ‌все‌ ‌данные,‌ ‌а‌ ‌программисту‌ ‌не‌ ‌придётся‌ ‌запоминать‌ ‌или‌ ‌записывать‌ ‌отдельно‌ ‌каждое‌ ‌значение.‌ ‌В‌ ‌языке‌ ‌С‌ ‌переменную‌ ‌нужно‌ ‌записывать‌ ‌так:‌ ‌

При‌ ‌неявной‌ ‌типизации‌ ‌тип‌ ‌переменной‌ ‌определяется‌ ‌интерпретатором‌ ‌или‌ ‌компилятором,‌ ‌поэтому‌ ‌записи‌ ‌в‌ ‌таких‌ ‌языках‌ ‌короче.‌ ‌Иногда‌ ‌они‌ ‌позволяют‌ ‌вручную‌ ‌указывать‌ ‌типы‌ ‌значений,‌ ‌как‌ ‌в‌ ‌Haskell‌ ‌или‌ ‌Python.‌ ‌В‌ ‌Python‌ ‌возможна‌ ‌такая‌ ‌запись,‌ ‌ведь‌ ‌язык‌ ‌сам‌ ‌определит,‌ ‌что‌ ‌это‌ ‌целое‌ ‌число:‌ ‌

Типизация‌ ‌в‌ ‌разных‌ ‌языках‌ ‌программирования‌ ‌

Типизация языков программирования: разбираемся в основах 1

Разные‌ ‌категории ‌могут‌ ‌пересекаться.‌ ‌Обычно‌ ‌языку‌ ‌программирования‌ ‌присуще‌ ‌одно‌ ‌значение‌ ‌из‌ ‌каждой‌ ‌группы:‌ ‌он‌ ‌может‌ ‌быть‌ ‌сильным‌ ‌или‌ ‌слабым,‌ ‌явным‌ ‌или‌ ‌неявным.‌ ‌Но‌ ‌есть‌ ‌несколько‌ ‌исключений.‌ ‌Так,‌ ‌Python‌ ‌с‌ ‌аннотациями‌ ‌может‌ ‌поддерживать‌ ‌явную‌ ‌типизацию‌,‌ ‌а‌ ‌язык‌ ‌D,‌ ‌наоборот,‌ ‌‌—‌ ‌неявную.‌ ‌С#‌ ‌поддерживает‌ ‌динамическую‌ ‌типизацию‌ ‌благодаря‌ ‌ключевым‌ ‌словам‌ ‌‌dynamic‌ ‌и‌ ‌var,‌ ‌Delphi‌ ‌—‌ ‌благодаря‌ ‌типу‌ ‌Variant,‌ ‌а‌ ‌С++‌ ‌—‌ ‌‌с‌ ‌помощью‌ ‌библиотеки‌ ‌Boost‌ ‌и‌ ‌имеет‌ ‌одновременно‌ ‌черты‌ ‌языка‌ ‌с‌ ‌сильной‌ ‌и‌ ‌
слабой‌ ‌типизацией.‌

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *