Изменяемые vs. неизменяемые типы данных в python

Системы типизации некоторых статических языков

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

  • C (1972), Go (2009): Эти системы совсем не мощные, без поддержки обобщенных типов. Невозможно задать тип MyList, который бы означал «список целых чисел», «список строк» и т.д. Вместо этого придется делать «список необозначенных значений». Программист должен вручную сообщать «это список строк» каждый раз, когда строка извлекается из списка, и это может привести к ошибке при исполнении.
  • Java (1995), C# (2000): Оба языка поддерживают обобщенные типы, так что можно сказать и получить список строк, о котором компилятор знает и может следить за соблюдением правил типов. Элементы из списка будут обладать типом String, компилятор будет форсировать правила при компиляции как обычно, так что ошибки при исполнении менее вероятны.
  • Haskell (1990), Rust (2010), Swift (2014): Все эти языки обладают несколькими продвинутыми возможностями, в том числе обобщенными типами, алгебраическими типами данных (ADTs), и классами типов или чем-то похожим (типы классов, признаки (traits) и протоколы, соответственно). Rust и Swift более популярны, чем Haskell, и их продвигают крупные организации (Mozilla и Apple, соответственно).
  • Agda (2007), Idris (2011): Эти языки поддерживают зависимые типы, позволяя создавать типы вроде «функция, которая принимает два целых числа х и y, где y больше, чем x». Даже ограничение «y больше, чем x» форсируется при компиляции. При выполнении y никогда не будет меньше или равно x, что бы ни случилось. Очень тонкие, но важные свойства системы могут быть проверены статически в этих языках. Их изучает очень мало программистов, но эти языки вызывают у них огромный энтузиазм.

Наблюдается явное движение в сторону более мощных систем типизации, особенно если судить по популярности языков, а не по простому факту существования языков. Известное исключение — это Go, который объясняет, почему многие сторонники статических языков считают его шагом назад.

Группа два (Java и C#) — это мэйнстримовые языки, зрелые и широко используемые.

Группа три находится на пороге входа в мэйнстрим, с большой поддержкой со стороны Mozilla (Rust) и Apple (Swift).

Группа четыре (Idris and Agda) далеки от мэйнстрима, но это может измениться со временем. Языки группы три были далеко от мэйнстрима еще десять лет назад.

Типы данных

Python — язык со строгой динамической типизацией. Строгая типизация означает, что переменная в любой момент времени имеет точно определенный тип. В языке Python тип переменной легко определить с помощью функции type:

Здесь знак > (=) — оператор присваивания. Это аналогично синтаксису языков C/C++/JAVA. Однако в отличие от них, в Python нет необходимости объявлять тип переменной. Он определяется автоматически в ходе выполнения программы (динамическая типизация. Более того, переменная может изменить свой тип в ходе выполненения программы, если ей будет присвоено новое значение другого типа. Как говорят, в языке Python >. Удобство такого подхода проявится, например, при написании функции. Таким образом, имена переменных в Python — это только ссылки на объекты. Они не хранят информацию о типе объекта.

К основным встроенныем типам данных языка Python относятся числа, строки, множества, списки, кортежи и словари. Рассмотрим их подробнее (в скобках будет указан тип данных, возвращаемый функцией type).

— Числовые типы, которые включают целые (int и long), вещественные (float) и комплексные (complex) числа.

— Строка (str) — последовательность символов Юникода. Строки заключаются в кавчики либо в апострафы (оба варианта равноценны). Например, «Python» или ‘Python’.

— Множество (set). Пример множества: {1,2,3}.

— Список (list) — упорядоченная последовательность значений произвольных типов. Пример списка: .

— Кортежь (tuple) — тоже список, только неизменяемый. Пример кортежа: (1,2,3).

— Словарь (dict) — неотсортированная колекция элементов, доступ к которым осуществляется по ключу. Пример словаря: {1:’a’, 2:’b’}. По большому счету словарь в Python — это список кортежей.

— Логический тип (bool), переменные которого могут принимать только два значения: True и False.

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

Продемонстрируем сказанное на примере класса int, реализующего целые числа. Для этого класса перегружены базовые операторы +,-,*, и ** (возвещение в степень), соответствующие основные арифметическим операция. Например, код

может быть записан в эквивалентном виде с использованием метода __add__() класса int:

Но, конечно, так никто не пишет.

Узнать полный список методов любого класса можно посредством функции dir, указав нужный класс в качестве аргумента этой функции. Например,

Для получения более детальной справки о классе и его методах используйте функцию help:

Среди полезных возможностей Python следует отметим возможность работы с комплексными числами. Мнимая единица задается как 1j либо 1J. Например,

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

%Однако его здесь рассматривать не будем. Вместо этого перейдем к обзору более сложных типов данных и операций над ними.

Неизменяемые типы данных

Давайте рассмотрим некоторые неизменяемые типы.

Целые числа (int)

Давайте определим переменную , имеющую значение 10. Встроенный метод используется для определения местоположения в памяти, а используется для определения типа переменной. Когда мы пытаемся изменить значение , оно успешно изменяется.

Стоит заметить, что адрес памяти тоже изменяется. Так происходит потому, что фактически мы не изменили значение , а создали другой объект с тем же именем и присвоили ему другое значение. Мы связали имя с новым значением. Теперь, когда вы вызываете , он будет выводить новое значение и ссылаться на новое местоположение.

x = 10
print(x, type(x), id(x))
# Получим (10, int, 140727505991744)
x = 12
print(x, type(x), id(x))
# Получим (12, int, 140727505991808)

Строки (str)

То же самое верно и для строкового типа данных. Мы не можем изменить существующую переменную, вместо этого мы должны создать новую с тем же именем.

В данном примере мы определили строковую переменную , но допустили ошибку в слове и теперь хотим исправить «ю» на «и». Однако мы получаем TypeError. Это показывает, что строковые объекты не подлежат обновлению.

x = 'Прювет!'
x = 'и'
# Получим TypeError: 'str' object does not support item assignment

Кортежи (tuple)

Давайте разберем кортежи. Мы определили кортеж с 4 значениями. Воспользуемся функцией для вывода его адреса. Если мы захотим изменить значение первого элемента, то получим ошибку TypeError. Это означает, что кортеж не поддерживает присвоение или обновление элементов.

tuple1 = (1, 2, 3, 4)
print(tuple1, id(tuple1))
# Получим (1, 2, 3, 4) 3240603720336

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

tuple1 = (5, 6, 7, 8)
print(tuple1, id(tuple1))
# Получим (5, 6, 7, 8) 3240603720256

Числа с плавающей запятой (float)

У нас есть переменная типа float. Используя функцию , мы можем узнать ее адрес. Если мы попробуем заменить элемент с индексом 1, то получим TypeError. Как и в предыдущих примерах видим, что float не поддерживает модификацию элемента.

x = 3.456
x = 4
# Получим TypeError: 'float' object does not support item assignment

Если же мы обновим float, переопределив его, то при вызове получим новое значение и новый адрес.

x = 3.654
type(x), id(x)
# Получим (float, 3240603166960)

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

Понятия «сильный» и «слабый» — очень неоднозначные. Вот некоторые примеры их использования:

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

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

  • Иногда «сильный» означает, что невозможно обойти строгие правила типизации в языке.

  • Иногда «сильный» означает безопасный для памяти (memory-safe).
    Си — это пример небезопасного для памяти языка. Если — это массив четырех чисел, то Си с радостью выполнит код или , возвращая какое-то значение из памяти, которая находится сразу за .

Давайте остановимся. Вот как некоторые языки отвечают этим определениям. Как можно заметить, только Haskell последовательно «сильный» по всем параметрам. Большинство языков не такие четкие.

Язык Статический? Неявные преобразования? Строгие правила? Безопасный для памяти?
C Сильный Когда как Слабый Слабый
Java Сильный Когда как Сильный Сильный
Haskell Сильный Сильный Сильный Сильный
Python Слабый Когда как Слабый Сильный
JavaScript Слабый Слабый Слабый Сильный

(«Когда как» в колонке «Неявные преобразования» означает, что разделение между сильным и слабым зависит от того, какие преобразования мы считаем приемлемыми).

Зачастую термины «сильный» и «слабый» относятся к неопределенной комбинации разных определений выше, и других, не показанных здесь определений. Весь этот беспорядок делает слова «сильный» и «слабый» практически бессмысленными. Когда хочется использовать эти термины, то лучше описать, что конкретно имеется ввиду. Например, можно сказать, что «JavaScript возвращает значение, когда складывается строка с числом, но Python возвращает ошибку». В таком случае мы не будем тратить свои силы на попытки прийти к соглашению о множестве значений слова «сильный». Или, еще хуже: придем к неразрешенному непониманию из-за терминологии.

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

Как написал Крис Смит:

Плюсы и минусы динамической типизации

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

Тем не менее недостатков при этом куда больше и они весьма существенны:

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

Создание динамических классов

Допустим, у нас есть следующие классы.

class Data:
    """Data Class"""
    d_id = 10


class SubData(Data):
    """SubData Class"""
    sd_id = 20

Напечатаем некоторые свойства этих классов.

print(Data.__class__)
print(Data.__bases__)
print(Data.__dict__)
print(Data.__doc__)

print(SubData.__class__)
print(SubData.__bases__)
print(SubData.__dict__)
print(SubData.__doc__)

Вывод:

<class 'type'>
(<class 'object'>,)
{'__module__': '__main__', '__doc__': 'Data Class', 'd_id': 10, '__dict__': <attribute '__dict__' of 'Data' objects>, '__weakref__': <attribute '__weakref__' of 'Data' objects>}
Data Class

<class 'type'>
(<class '__main__.Data'>,)
{'__module__': '__main__', '__doc__': 'SubData Class', 'sd_id': 20}
SubData Class

Мы можем создавать похожие классы с помощью функции type().

Data1 = type('Data1', (object,), {'__doc__': 'Data1 Class', 'd_id': 10})
SubData1 = type('SubData1', (Data1,), {'__doc__': 'SubData1 Class', 'sd_id': 20})

print(Data1.__class__)
print(Data1.__bases__)
print(Data1.__dict__)
print(Data1.__doc__)

print(SubData1.__class__)
print(SubData1.__bases__)
print(SubData1.__dict__)
print(SubData1.__doc__)

Вывод:

<class 'type'>
(<class 'object'>,)
{'__doc__': 'Data1 Class', 'd_id': 10, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'Data1' objects>, '__weakref__': <attribute '__weakref__' of 'Data1' objects>}
Data1 Class

<class 'type'>
(<class '__main__.Data1'>,)
{'__doc__': 'SubData1 Class', 'sd_id': 20, '__module__': '__main__'}
SubData1 Class

Обратите внимание, что мы не можем создавать функции в динамическом классе с помощью функции type()

Функции

Как и в большинстве языков программирования, в Python можно определять собственные функции. Для создания функции предназначена5 инструкция def. Ниже показан пример определения функции:

По умолчанию все переменные внутри функции локальные, т.е. находится в локальной области видимости. Это вполне естественный подход. Однако есть возможность обратиться и к глобальным переменным, объявленным вне функции. Для этого имеются ключевые слова global и nonlocal.

Еще одни способ задания функций — использование лямбда-нотации:

Или даже так, без присвоения имени функции:

Такие анонимные (или лямбда-функции) составляют основу функционального программирования.

Утиный ввод в Python

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

Давайте посмотрим на это на примере настраиваемого объекта и функции add(), которую мы определили.

def add(x, y):
    return x + y

class Data:

    def __init__(self, i):
        self.id = i

d1 = Data(10)
d2 = Data(5)

print(add(d1, d2))

Этот код вызовет следующую ошибку времени выполнения:

Traceback (most recent call last):
  File "/Users/pankaj/Documents/PycharmProjects/hello-world/journaldev/type_checking.py", line 12, in <module>
    print(add(d1, d2))
  File "/Users/pankaj/Documents/PycharmProjects/hello-world/journaldev/type_checking.py", line 2, in add
    return x + y
TypeError: unsupported operand type(s) for +: 'Data' and 'Data'

Если мы хотим, чтобы наш объект поддерживал оператор сложения, все, что нам нужно сделать, это определить для него функцию __add __().

def __add__(self, other):
    return self.id + other.id

Теперь оператор печати напечатает 15, и код не вызовет ошибок. Так что, по сути, тип объекта вообще не имеет значения. Пока определены необходимые функции для поддержки операции, проблем из-за типа объекта не возникнет.

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

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

def calculate(x, y, op='sum'):
    if op == 'divide':
        return x // y
    if op == 'difference':
        return x - y
    if op == 'multiply':
        return x * y
    # default is sum
    return x + y

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

print(calculate(10, 3, 'divide'))  # 3
print(calculate(10, 5))  # 15
print(calculate('A', 'B'))  # AB

Давайте посмотрим, как добавить подсказки типа для данной функции.

def calculate1(x: int, y: int, op: str = 'sum') -> int:
    # same code as above

Подсказки типа аргумента функции снабжены двоеточием (:), а тип возвращаемого значения – знаком ->.

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

Но сторонние инструменты, такие как средства проверки типов, IDE, линтеры и т.д., могут анализировать это, чтобы предупредить нас о возможности неправильных типов аргументов. Например, если мы передадим строковые аргументы этой функции, PyCharm IDE выдаст предупреждающее сообщение как «Ожидаемый тип int, вместо этого получил str».

Компиляция статически типизированного кода

Когда происходит компиляция статически типизированного кода, сначала проверяется синтаксис, как в любом компиляторе. Потом проверяются типы. Это означает, что статический язык сначала может пожаловаться на одну синтаксическую ошибку, а после ее исправления пожаловаться на 100 ошибок типизации. Исправление синтаксической ошибки не создало эти 100 ошибок типизации. Компилятор просто не имел возможности обнаружить ошибки типов, пока не был исправлен синтаксис.

Компиляторы статических языков обычно могут генерировать более быстрый код, чем компиляторы динамических. Например, если компилятор знает, что функция add принимает целые числа, то он может использовать нативную инструкцию ADD центрального процессора. Динамический язык будет проверять тип при выполнении, выбирая один из множества функций add в зависимости от типов (складываем integers или floats или склеиваем строки или, может быть, списки?) Или нужно решить, что возникла ошибка и типы не соответствуют друг другу. Все эти проверки занимают время. В динамических языках используются разные трюки для оптимизации, например JIT-компиляция (just-in-time), где код перекомпилируется при выполнении после получения всей необходимой о типах информации. Однако, никакой динамический язык не может сравниться по скоростью с аккуратно написанным статическим кодом на языке вроде Rust.

Последовательности

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

str (строка)

Строки, пожалуй, единственный объект, который может сравниться по степени своей используемости с числовым типом данных. Тавтологическое, но полное определение, справедливое для Python звучит так:

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

Строки, строки everywhere!

Больше информации по строкам в Python тут:

Строки в Python и функции для работы с ними

list (список)

Список — это ещё один вид последовательностей… Здесь стоит остановиться и отметить, что последовательности в Python бывают изменяемыми и неизменяемыми. Список — изменяемая последовательность, а строки и кортежи — нет. Таким образом, список можно определить, как упорядоченную и изменяемую коллекцию, состоящую из объектов произвольных типов.

Само название списков говорит об их предназначении быть объектами для хранения наборов данных. Список покупок, подарков, результатов матчей, ip клиентов или объектов типа Student. Списки в Python — это эдакие массивы из прочих языков «на максималках».

Подробнее про работу со списками читайте тут:

Работа со списками (list) в Python

tuple (кортеж)

Кортежи в языке Python можно рассматривать, как неизменяемые списки со всеми вытекающими:

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

Подробнее о кортежах в Python:

Кортежи в Python (tuple)

Постепенная типизация (gradual typing)

Можно ли добавить статические типы в динамические языки? В некоторых случаях — да. В других это сложно или невозможно. Самая очевидная проблема — это и другие похожие возможности динамических языков. Выполнение в Python дает 3. Но что даст ? Это зависит от того, что в сети на момент выполнения. Если получим число, то выражение корректно. Если строку, то нет. Невозможно узнать до запуска, так что невозможно анализировать тип статически.

Неудовлетворительное решение на практике — это задать выражению тип Any, что напоминает Object в некоторых объектно-ориентированных языках программирования или интерфейс в Go: это тип, которому удовлетворяет любое значение.

Значения типа Any не ограничены ничем, так что исчезает возможность системы типов помогать нам в коде с eval. Языки, в которых есть и и система типов, должны отказываться от безопасности типов при каждом использовании .

В некоторых языках есть опциональная или постепенная типизация (gradual typing): они динамические по умолчанию, но позволяют добавлять некоторые статические аннотации. В Python недавно добавили опциональные типы; TypeScript — это надстройка над JavaScript, в котором есть опциональные типы; Flow производит статический анализ старого доброго кода на JavaScript.

Эти языки предоставляют некоторые преимущества статической типизации, но они никогда не дадут абсолютной гарантии, как по-настоящему статические языки. Некоторые функции будут статически типизированными, а некоторые будут динамически типизированными. Программисту всегда нужно знать и опасаться разницы.

Различия между языками программирования

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

Например:

  • Java , Pascal , Go , Ada и C требуют, чтобы все переменные имели объявленный тип, и поддерживают использование явного приведения арифметических значений к другим арифметическим типам. Иногда говорят, что Java, C #, Ada и Pascal имеют более строгую типизацию, чем C; это утверждение, вероятно, основано на том факте, что C поддерживает больше видов неявных преобразований, а C также позволяет явно приводить значения указателей, в то время как Java и Pascal не надо. Сама Java может считаться более строго типизированной, чем Паскаль, поскольку способы обхода системы статических типов в Java контролируются системой типов виртуальной машины Java . C # и VB.NET похожи на Java в этом отношении, хотя они позволяют отключать динамическую проверку типов, явно помещая сегменты кода в «небезопасный контекст». Система типов Паскаля была описана как «слишком сильная», потому что размер массива или строки является частью ее типа, что делает некоторые задачи программирования очень сложными.
  • Smalltalk , Perl , Ruby , Python и Self являются «строго типизированными» в том смысле, что ошибки ввода предотвращаются во время выполнения и они не выполняют неявное преобразование типов , но эти языки не используют статическую проверку типов: компилятор не проверяет или обеспечить соблюдение правил ограничения типа. Термин « утиная типизация» теперь используется для описания парадигмы динамической типизации, используемой языками этой группы.
  • Все языки семейства Lisp являются «строго типизированными» в том смысле, что ошибки ввода предотвращаются во время выполнения. Некоторые диалекты Lisp, такие как Common Lisp или Clojure, действительно поддерживают различные формы объявлений типов, а некоторые компиляторы ( CMUCL и родственные) используют эти объявления вместе с выводом типа, чтобы обеспечить различные оптимизации, а также ограниченные формы проверки типов во время компиляции.
  • Стандартные ML , F # , OCaml , Haskell и Rust проверяются статически, но компилятор автоматически определяет точный тип для большинства значений.
  • Ассемблер и Форт можно охарактеризовать как нетипизированные . Нет проверки типа; программист должен гарантировать, что данные, передаваемые функциям, имеют соответствующий тип. Любое требуемое преобразование типа является явным.

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

Файл

Работа с файлами, хранящимися где-то на внешнем носителе, в Python реализована в виде объектов-файлов. Они относятся к объектам базового типа, но обладают весьма характерной чертой: нельзя создать экземпляр объекта-файла при помощи литералов.

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

Операции с файлами могут быть разными, а, следовательно, разными могут быть и режимы работы с ними:

  • — выбирается по умолчанию, означает открытие файла для чтения;
  • — файл открывается для записи (если не существует, то создаётся новый);
  • — файл открывается для записи (если не существует, то генерируется исключение);
  • — режим записи, при котором информация добавляется в конец файла, а не затирает уже имеющуюся;
  • — открытие файла в двоичном режиме;
  • — ещё одно значение по умолчанию, означающее открытие файла в текстовом режиме;
  • — читаем и записываем.

9 ответов

Я только что обнаружил превосходный лаконичный способ запомнить его:

1

Raining
8 Май 2019 в 13:38

Я думаю, этот простой пример должен объяснить различия между строгой и динамической типизацией:

Ява:

Dmitry Zagorulkin
4 Июл 2012 в 13:35

В соответствии с этим > вики Python статья Python динамически и строго типизирован (также дает хорошее объяснение).

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

Этот вопрос SO может представлять интерес: Языки динамических типов и языки статических типов и эта статья в Википедии Системы типов содержит дополнительную информацию

24

Community
23 Май 2017 в 12:34

Переменная Python хранит нетипизированную ссылку на целевой объект, который представляет значение.

Любая операция присваивания означает присвоение нетипизированной ссылки назначенному объекту, то есть объект совместно используется посредством исходных и новых (подсчитанных) ссылок.

Тип значения привязан к целевому объекту, а не к контрольному значению. Проверка (строгого) типа выполняется при выполнении операции со значением (время выполнения).

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

6

pepr
4 Июл 2012 в 13:15

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

-4

Ryan Alexander
2 Авг 2013 в 17:40

На него уже отвечали несколько раз, но Python — это строго типизированный язык:

Следующее в JavaScript:

В этом разница между слабой и строгой типизацией. Слабые типы автоматически пытаются преобразовать один тип в другой, в зависимости от контекста (например, Perl). Сильные типы никогда конвертируются неявно.

Ваша путаница заключается в неправильном понимании того, как Python связывает значения с именами (обычно называемыми переменными).

В Python имена не имеют типов, поэтому вы можете делать такие вещи, как:

И имена могут быть связаны с чем угодно:

Для дальнейшего чтения:

И немного связанный, но более продвинутый:

8

Wayne Werner
4 Июл 2012 в 12:34

Термин «строгая типизация» не имеет определенного определения.

Поэтому использование термина зависит от того, с кем вы разговариваете.

Я не рассматриваю ни один язык, в котором тип переменной не объявлен явно или статически типизирован для строгой типизации.

Строгая типизация не просто исключает преобразование (например, «автоматическое» преобразование из целого числа в строку). Это исключает присваивание (т. Е. Изменение типа переменной).

Если следующий код компилирует (интерпретирует), язык не является строго типизированным:

Foo = 1 Foo = «1»

В строго типизированном языке программист может «рассчитывать» на тип.

Например, если программист видит объявление,

UINT64 kZarkCount;

И он или она знает, что через 20 строк kZarkCount по-прежнему является UINT64 (если это происходит в одном и том же блоке) — без проверки промежуточного кода.

6

user5330045
13 Сен 2015 в 05:20

Вы путаете «строго типизированный» с ‘динамически типизированный’.

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

Противоположностью динамической типизации является статическая типизация; объявление типов переменных не изменяется во время жизни программы. Противоположностью строгой типизации является слабая типизация; тип значений может меняться в течение времени жизни программы.

36

Martijn Pieters
4 Июл 2012 в 12:23

TLDR ;

Python набирает Динамический , поэтому вы можете заменить переменную int на строку

Типизация Python Сильная , поэтому вы не можете объединять типы:

В слабо типизированном Javascript это происходит …

Относительно вывода типа

Java заставляет вас явно объявлять ваши типы объектов

Kotlin использует логический вывод, чтобы понять, что это

Но поскольку оба языка используют типы статические , нельзя заменить на . Ни один из этих языков не позволил бы динамически изменить

15

Adam Hughes
1 Мар 2019 в 14:49

Что касается вывода типа

Java заставляет вас явно объявлять типы ваших объектов

Котлин использует вывод, чтобы понять, что это

Но поскольку оба языка используют статический типы, нельзя изменить с . Ни один из языков не позволил бы динамичный изменить как

  • Я не знаю подробностей Javascript, но может быть перегрузка и преобразование типов за сценой?
  • 4 Как бы то ни было, ваш ответ на самом деле более краткий и понятный, чем приведенный выше.

На него уже несколько раз ответили, но Python — это строго типизированный язык:

Следующее в JavaScript:

В этом разница между слабой типизацией и строгой типизацией. Слабые типы автоматически пытаются преобразовать один тип в другой в зависимости от контекста (например, Perl). Сильные типы никогда неявно конвертировать.

Ваше замешательство заключается в неправильном понимании того, как Python связывает значения с именами (обычно называемыми переменными).

В Python имена не имеют типов, поэтому вы можете делать такие вещи, как:

И имена могут быть привязаны к чему угодно:

Для дальнейшего чтения:

https://en.wikipedia.org/wiki/Dynamic_dispatch

и немного связанные, но более продвинутые:

http://effbot.org/zone/call-by-object.htm

  • 1 Спустя несколько лет — еще один полезный и актуальный ресурс: youtu.be/_AEJHKGk9ns
  • Сильная и слабая типизация не имеет ничего общего с типом результата таких выражений, как 3 + ‘4’. В этом примере JavaScript так же силен, как и Python.
  • @qznc, почему Javasript так же силен? Не думаю, что я имел в виду, что это имеет какое-либо отношение к результирующему типу, на самом деле я прямо заявляю Слабые типы автоматически пытаются преобразовать из одного типа в другой.
  • 2 @oneloop это не обязательно верно, просто поведение для объединения чисел с плавающей запятой и целых чисел четко определено и приводит к появлению числа с плавающей запятой. Ты можешь сделать в питоне тоже. Результат конечно же . Вы бы не сказали, что это преобразование того и другого. Конечно, это могло быть просто аргументом в пользу семантики.
  • 1 @oneloop Необязательно, что поскольку Python производит из комбинации и что он неявно преобразует тип. Между float и int существует естественная связь, и, действительно, иерархия типов объясняет это. Я полагаю, вы можете утверждать, что Javascript считает и чтобы обе были четко определенными операциями так же, как Python считает быть четко определенным, но в этот момент действительно не существует таких вещей, как сильные или слабые типы, только (не) определенные операции.

Термин «строгая типизация» не имеет определенного определения.

Следовательно, использование этого термина зависит от того, с кем вы говорите.

Я не рассматриваю язык, в котором тип переменной не объявлен явно или не статически типизирован, как строго типизированный.

Строгая типизация не просто исключает преобразование (например, «автоматическое» преобразование целого числа в строку). Это исключает присвоение (т.е. изменение типа переменной).

Если следующий код компилируется (интерпретируется), язык не является строго типизированным:

Foo = 1 Foo = «1»

В строго типизированном языке программист может «рассчитывать» на тип.

Например, если программист видит объявление,

UINT64 kZarkCount;

и он или она знает, что 20 строк спустя kZarkCount по-прежнему является UINT64 (пока он встречается в том же блоке) — без необходимости проверять промежуточный код.

Переменная Python хранит нетипизированную ссылку на целевой объект, представляющий значение.

Любая операция присваивания означает присвоение нетипизированной ссылки назначенному объекту, то есть объект совместно используется через исходную и новую (подсчитанную) ссылки.

Тип значения привязан к целевому объекту, а не к ссылочному значению. (Строгая) проверка типа выполняется, когда выполняется операция со значением (время выполнения).

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

Я только что открыл для себя превосходный способ запомнить его:

Думаю, этот простой пример поможет вам объяснить разницу между строгой и динамической типизацией:

Ява:

  • Ваш код python демонстрирует динамическую типизацию, в то время как java демонстрирует статическую типизацию. Лучшим примером будет $ var = ‘2’ + 1 // результат 3
  • @ivleph я согласен. также можно написать что-то вроде этого: «a» * 3 == «aaa»

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

Итог

Я считаю, что SonarLint более полезный, чем основные методы блокировки и линтинга. Я также убеждён в том, что он помогает мне писать более человеко-ориентированный код (между прочим, на Python).

SonarLint бесплатный, поэтому нет никаких причин откладывать его использование.

Заключение

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

Три секретных оружия:

  1. Kite: поможет писать быстрее и реже пользоваться Google.
  2. Mypy: позволяет стабилизировать код.
  3. SonarLint: быстрый перехват ошибок и упрощённое написание функций.

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

  • Изучаем Python: генераторы, стримы и yield
  • Потоковые и многопроцессорные модули на Python
  • Вы умеете говорить на Python?

Читайте нас в телеграмме, vk и

Рейтинг
( Пока оценок нет )
Понравилась статья? Поделиться с друзьями:
Все про сервера
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: