Является ли Python строго типизированным?

234

Я наткнулся на ссылки, в которых написано, что Python - язык строго типизированный.

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

bob = 1
bob = "bob"

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

Итак, является ли Python сильно или слабо типизированным языком?

Pacane
источник

Ответы:

359

Python сильно, динамически типизирован.

  • Строгая типизация означает, что тип значения не меняется неожиданным образом. Строка, содержащая только цифры, волшебным образом не становится числом, как в Perl. Каждое изменение типа требует явного преобразования.
  • Динамическая типизация означает, что объекты (значения) времени выполнения имеют тип, в отличие от статической типизации, где переменные имеют тип.

Что касается вашего примера

bob = 1
bob = "bob"

Это работает, потому что переменная не имеет типа; он может назвать любой объект. После bob=1вы найдете, что type(bob)возвращается int, но после bob="bob", он возвращается str. (Обратите внимание, что typeэто обычная функция, поэтому она оценивает свой аргумент, а затем возвращает тип значения.)

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

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

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

def to_number(x):
    """Try to convert function argument to float-type object."""
    try: 
        return float(x) 
    except (TypeError, ValueError): 
        return 0 

class Foo:
    def __init__(self, number): 
        self.number = number

    def __add__(self, other):
        return self.number + to_number(other)

Экземпляр класса Fooможет быть добавлен к другим объектам:

>>> a = Foo(42)
>>> a + "1"
43.0
>>> a + Foo
42
>>> a + 1
43.0
>>> a + None
42

Заметим , что даже если сильно типизированных Python полностью хорошо с добавлением объектов типа intи floatи возвращает объект типа float(например, int(42) + float(1)возвращается 43.0). С другой стороны, из-за несоответствия между типами Haskell будет жаловаться, если попытаться сделать следующее (42 :: Integer) + (1 :: Float). Это делает Haskell строго типизированным языком, где типы полностью не пересекаются, и через классы типов возможна только контролируемая форма перегрузки.

Fred Foo
источник
18
Один пример, который я не вижу часто, но я думаю, важно показать, что Python не является полностью строго типизированным, это все, что оценивается как логическое: docs.python.org/release/2.5.2/lib/truth.html
gsingh2011
25
Не уверен, что это контрпример: вещи могут быть оценены как логические, но они не вдруг «становятся» логическими. Это почти как если бы кто-то неявно назвал что-то вроде as_boolean (<value>), что не совпадает с типом изменяемого объекта, верно?
jbrendel
15
Быть правдивым в логическом контексте не является контрпримером, потому что на самом деле ничего не конвертируется в Trueили False. Но как насчет продвижения номера? 1.0 + 2в Python работает так же хорошо, как в Perl или C, хотя "1.0" + 2и не работает. Я согласен с @jbrendel, что это на самом деле не неявное преобразование, это просто перегрузка - но в том же смысле Perl также не выполняет никакого неявного преобразования. Если функции не имеют объявленных типов параметров, неявное преобразование происходит некуда.
abarnert
13
Лучший способ думать о строгой типизации - это то, что тип имеет значение при выполнении операций с переменной. Если тип не соответствует ожидаемому, то язык, который жалуется, строго типизирован (python / java), а язык, который не является слабо типизированным (javascript) Динамически типизированные языки (python) - это те, которые позволяют типу переменной изменяться в время выполнения, тогда как статически типизированные языки (java) не допускают этого после объявления переменной.
Кашиф
2
@ gsingh2011 Правдивость полезна, а не слабая печатание сама по себе, но случайная if isValid(value) - 1утечка может произойти. Логическое значение приводится к целому числу, которое затем оценивается как истинное значение. False - 1становится правдивым и True - 1ложным, что приводит к смущающе сложной двухслойной ошибке «от одного к одному» для отладки. В этом смысле Python в основном строго типизирован; Приведение типов обычно не вызывает логических ошибок.
Aaron3468
57

Есть некоторые важные вопросы, которые, я думаю, все существующие ответы упустили.


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

char sz[] = "abcdefg";
int *i = (int *)sz;

На платформе с прямым порядком байтов с 32-разрядными целыми числами это превращается iв массив чисел 0x64636261и0x00676665 . На самом деле, вы можете даже привести указатели к целым числам (соответствующего размера):

intptr_t i = (intptr_t)&sz;

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

char *spam = (char *)0x12345678
spam[0] = 0;

* Конечно, современные ОС используют виртуальную память и защиту страниц, так что я могу перезаписывать только память своего собственного процесса, но в самом C нет ничего, что предлагало бы такую ​​защиту, как вам скажет любой, кто когда-либо программировал, скажем, на Classic Mac OS или Win16.

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

Большинство языков сегодня не так слабы, как C и Lisp, но многие из них все еще несколько утечек. Например, любой ОО-язык, который имеет неконтролируемый «downcast», * это утечка типа: вы, по сути, говорите компилятору: «Я знаю, я не дал вам достаточно информации, чтобы знать, что это безопасно, но я уверен, что это так, «когда весь смысл системы типов заключается в том, что компилятор всегда имеет достаточно информации, чтобы знать, что безопасно.

* Проверенное снижение не делает систему типов языка более слабой только потому, что она переводит проверку во время выполнения. Если бы это было так, то полиморфизм подтипов (то есть виртуальные или полностью динамические вызовы функций) был бы таким же нарушением системы типов, и я не думаю, что кто-то хочет это сказать.

В этом смысле очень немногие языки сценариев являются слабыми. Даже в Perl или Tcl вы не можете взять строку и просто интерпретировать ее байты как целое число. * Но стоит отметить, что в CPython (и аналогично для многих других интерпретаторов для многих языков), если вы действительно постоянны, вы можно использовать ctypesдля загрузки libpython, приведения объекта idк объекту POINTER(Py_Object)и принудительной утечки системы типов. Делает ли это систему типов слабой или нет, зависит от ваших вариантов использования - если вы пытаетесь внедрить изолированную программную среду с ограниченным исполнением на языке для обеспечения безопасности, вам придется иметь дело с подобными побегами…

* Вы можете использовать такую ​​функцию, как struct.unpackчтение байтов и построение нового int из «того, как C будет представлять эти байты», но это явно не утечка; даже Haskell позволяет это.


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

Каждый язык, даже Haskell, имеет функции, скажем, для преобразования целого числа в строку или число с плавающей точкой. Но некоторые языки будут выполнять некоторые из этих преобразований автоматически - например, в C, если вы вызываете функцию, которая хочет float, и вы передаете ее int, она преобразуется для вас. Это может определенно привести к ошибкам, например, с неожиданным переполнением, но это не те ошибки, которые вы получаете от системы слабого типа. И С здесь на самом деле не слабее; Вы можете добавить int и float в Haskell или даже объединить float в строку, вам просто нужно сделать это более явно.

А с динамическими языками это довольно мутно. В Python или Perl нет такой вещи, как «функция, которая хочет плавать». Но есть перегруженные функции, которые делают разные вещи с разными типами, и есть сильный интуитивный смысл, что, например, добавление строки к чему-то другому - это «функция, которая хочет строку». В этом смысле Perl, Tcl и JavaScript, по-видимому, выполняют много неявных преобразований ( "a" + 1дает вам "a1"), тогда как Python делает намного меньше ( "a" + 1вызывает исключение, но 1.0 + 1дает вам 2.0*). Просто трудно выразить этот смысл в формальных терминах - почему не должно быть выражения, +которое принимает строку и целое число, когда есть, очевидно, другие функции, такие как индексация, которые делают?

На самом деле, в современном Python это можно объяснить с помощью подтипов ОО, поскольку isinstance(2, numbers.Real)это правда. Я не думаю, что есть какой-то смысл в том, что 2является экземпляром строкового типа в Perl или JavaScript ... хотя в Tcl это действительно так, поскольку все является экземпляром строки.


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

Например, Haskell позволяет вам определить тип, который является числом, строкой, списком этого типа или картой из строк в этот тип, что является отличным способом для представления всего, что может быть декодировано из JSON. Нет способа определить такой тип в Java. Но по крайней мере в Java есть параметрические (универсальные) типы, так что вы можете написать функцию, которая принимает список T и знает, что элементы имеют тип T; другие языки, такие как ранняя Java, заставляли вас использовать List of Object и downcast. Но, по крайней мере, Java позволяет создавать новые типы с помощью собственных методов; C только позволяет создавать структуры. И у BCPL даже этого не было. И так далее до сборки, где единственные типы имеют разную длину в битах.

Таким образом, в этом смысле система типов Haskell сильнее, чем современная Java, которая сильнее, чем более ранняя Java, которая сильнее, чем C, которая сильнее, чем BCPL.

Итак, где Python вписывается в этот спектр? Это немного сложно. Во многих случаях типирование утки позволяет имитировать все, что вы можете делать в Haskell, и даже некоторые вещи, которые вы не можете; Конечно, ошибки отлавливаются во время выполнения, а не во время компиляции, но они все еще отлавливаются. Тем не менее, есть случаи, когда утки не достаточно. Например, в Haskell вы можете сказать, что пустой список целых является списком целых, так что вы можете решить, что сокращение +по этому списку должно возвращать 0 *; в Python пустой список является пустым списком; нет информации о типах, которая бы помогла вам решить, что +делать с уменьшением .

* На самом деле, Haskell не позволяет вам сделать это; если вы вызываете функцию Reduce, которая не принимает начальное значение в пустом списке, вы получите ошибку. Но его система типов достаточно мощная, чтобы вы могли сделать это, а Python - нет.

abarnert
источник
3
Этот ответ блестящий! Жаль, что он так долго томился внизу списка.
LeoR
1
Небольшой комментарий к вашему примеру C: char sz[]это не указатель на char, это массив char, и в присваивании он превращается в указатель.
majkel.mk
39

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

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

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

Мартейн Питерс
источник
Описание в ссылке строго типизировано: «Вообще, язык со строгой типизацией имеет более строгие правила типизации во время компиляции, что подразумевает, что ошибки и исключения более вероятны во время компиляции». подразумевает, что Python является слабо типизированным языком ..., не так ли вики?
Дождь
1
@ s̮̦̩e̝͓c̮͔̞ṛ̖̖e̬̣̦t̸͉̥̳̼: это вовсе не подразумевается. Python имеет строгие правила типизации во время компиляции, каждый созданный объект имеет только один тип. И «вообще» ничего не подразумевает, это просто означает, что Python является исключением из этого.
Мартин Питерс
24

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

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

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

Левон
источник
18

TLDR;

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

x = 'somestring'
x = 50

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

'foo' + 3 --> TypeError: cannot concatenate 'str' and 'int' objects

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

 'foo'+3 = 'foo3'

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

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

int x = 50

Котлин использует умозаключения, чтобы понять, что этоint

x = 50

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

x = 50
x = 'now a string'
Адам Хьюз
источник
Я не знаю деталей Javascript, но 'x' + 3может быть operator+перегружен и выполняет преобразование типов за сценой?
дождь
3
Во всяком случае, ваш ответ на самом деле является более кратким и простым для понимания, чем приведенные выше.
Дождь
8

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

>>> x = 3
>>> y = '4'
>>> print(x+y)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'

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

var x = 3    
var y = '4'
alert(x + y) //Produces "34"

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

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

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

bob = 1
bob = "bob"
bob = "An Ex-Parrot!"

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

>>> def spam():
...     print("Spam, spam, spam, spam")
...
>>> spam_on_eggs = spam
>>> spam_on_eggs()
Spam, spam, spam, spam

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

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

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

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

Уэйн Вернер
источник
1
Спустя несколько лет - еще один полезный и актуальный ресурс: youtu.be/_AEJHKGk9ns
Уэйн Вернер
Строгая или слабая типизация не имеет ничего общего с типом результата выражений типа 3 + '4'. JavaScript такой же сильный, как Python для этого примера.
qznc
@qznc, как Javasript такой же сильный? Я не верю, что намекал, что это как-то связано с результирующим типом, на самом деле я явно заявляю, что Слабые типы автоматически пытаются конвертировать из одного типа в другой .
Уэйн Вернер
2
@oneloop, это не обязательно верно, просто поведение объединения чисел с плавающей точкой хорошо определено, и в результате получается плавание. Вы также можете сделать это "3"*4на Python. Результат, конечно, есть "3333". Вы не сказали бы, что это преобразовывает любую вещь. Конечно, это может быть просто спор о семантике.
Уэйн Вернер
1
@oneloop Это не обязательно так, потому что Python производит floatиз комбинации floatи intчто он неявно преобразует тип. Между float и int существует естественная связь, и действительно, тип heirarchy говорит об этом. Я полагаю, что вы могли бы утверждать, что Javascript рассматривает '3'+4и 'e'+4оба являются четко определенными операциями так же, как Python считает 3.0 + 4хорошо определенными, но в тот момент действительно не существует такого понятия, как сильный или слабый тип, просто (не) определенный операции.
Уэйн Вернер
6

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

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

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

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

pepr
источник
6

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

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

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

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

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

Foo = 1 Foo = "1"

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

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

UINT64 kZarkCount;

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

user5330045
источник
1

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

Динамическое / статическое типирование; строго / слабо типизированное значение.

Льется
источник
0

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

>>> tup = ('1', 1, .1)
>>> for item in tup:
...     type(item)
...
<type 'str'>
<type 'int'>
<type 'float'>
>>>

Ява:

public static void main(String[] args) {
        int i = 1;
        i = "1"; //will be error
        i = '0.1'; // will be error
    }
Дмитрий Загорулькин
источник
Ваш код Python демонстрирует динамическую типизацию, в то время как Java демонстрирует статическую типизацию. Лучшим примером будет $ var = '2' + 1 // результат 3
erichlf
@ivleph, я согласен. также можно написать что-то вроде этого: "a" * 3 == "aaa"
Дмитрий Загорулькин
-4
class testme(object):
    ''' A test object '''
    def __init__(self):
        self.y = 0

def f(aTestMe1, aTestMe2):
    return aTestMe1.y + aTestMe2.y




c = testme            #get a variable to the class
c.x = 10              #add an attribute x inital value 10
c.y = 4               #change the default attribute value of y to 4

t = testme()          # declare t to be an instance object of testme
r = testme()          # declare r to be an instance object of testme

t.y = 6               # set t.y to a number
r.y = 7               # set r.y to a number

print(f(r,t))         # call function designed to operate on testme objects

r.y = "I am r.y"      # redefine r.y to be a string

print(f(r,t))         #POW!!!!  not good....

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

Райан Александр
источник