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


я наткнулся на ссылки, которые говорят, что Python является строго типизированным языком.

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

bob = 1
bob = "bob"

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

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

10 178

10 ответов:

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 x to a number."""
    if x is None:
        return 0
    # more special cases here
    else:
        return float(x)  # works for numbers and strings

class Foo(object):
    def __add__(self, other):
        other = to_number(other)
        # now do the addition

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

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


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

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

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

intptr_t i = (intptr_t)&sz;

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

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

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

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

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

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

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

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


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

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

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

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


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

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

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

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

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

Вы путаете 'строго' С 'динамически'.

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

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

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

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

этот так вопрос может представлять интерес:языки динамического типа в сравнении со статическим типом языки и эта статья в Википедии о Системы Типа предоставляет больше информации

на него уже ответили несколько раз, но 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

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

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

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

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

термин "сильная типизация" не имеет определенного определения.

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

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

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

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

Foo = 1 Foo = "1"

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

например, если программист видит декларации

UINT64 kZarkCount;

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

TLDR;

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

x = 'somestring'
x = 50

Python typing is сильный так что вы не можете объединить типы:

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

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

 'x'+3 = 'x3'

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

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

int x = 50

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

x = 50

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

x = 50
x = 'now a string'

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

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

java:

public static void main(String[] args) {
        int i = 1;
        i = "1"; //will be error
        i = '0.1'; // will be error
    }
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....

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