파이썬은 강하게 타이핑됩니까?


234

파이썬은 강력한 유형의 언어라는 링크를 발견했습니다.

그러나 나는 당신이 할 수없는 강력한 유형의 언어로 생각했습니다 :

bob = 1
bob = "bob"

강력한 형식의 언어가 런타임에 형식 변경을 허용하지 않는다고 생각했습니다. 어쩌면 나는 강하거나 약한 유형에 대한 잘못된 (또는 너무 단순한) 정의를 얻었을 것입니다.

그렇다면 파이썬은 강력하거나 약한 유형의 언어입니까?

답변:


358

파이썬은 강력하고 역동적으로 타이핑됩니다.

  • 강력한 타이핑은 값의 유형이 예기치 않은 방식으로 변경되지 않음을 의미합니다. Perl에서와 같이 숫자 만 포함 된 문자열은 마술처럼 숫자가되지 않습니다. 모든 유형의 변경에는 명시적인 변환이 필요합니다.
  • 동적 유형 지정은 변수에 유형이있는 정적 유형 지정과 달리 런타임 객체 (값)에 유형이 있음을 의미합니다.

당신의 예는

bob = 1
bob = "bob"

변수에 유형이 없기 때문에 작동합니다. 모든 개체의 이름을 지정할 수 있습니다. 이후 bob=1에는을 type(bob)반환 int하지만 이후 bob="bob"에는을 반환합니다 str. ( type이것은 일반 함수이므로 인수를 평가 한 다음 값의 유형을 반환합니다.)

이것을 약하고 정적으로 타이핑 한 C의 오래된 방언과는 대조적으로, 포인터와 정수는 거의 상호 교환 가능합니다. (현대 ISO C는 대부분의 경우 변환이 필요하지만 기본적으로 컴파일러는 여전히 관대합니다.)

강력한 타이핑과 약한 타이핑은 부울 선택보다 연속체라고 덧붙여 야합니다. C ++는 C보다 타이핑이 더 강력하지만 (더 많은 변환이 필요함) 포인터 캐스트를 사용하여 유형 시스템을 전복시킬 수 있습니다.

파이썬과 같은 동적 언어에서 형식 시스템의 강점은 기본 형식과 라이브러리 함수가 다른 형식에 어떻게 반응하는지에 따라 결정됩니다. 예를 들어 +오버로드되어 두 개의 숫자 또는 두 개의 문자열에서 작동하지만 문자열과 숫자는 작동 하지 않습니다. 이것은 +구현 되었을 때 만들어진 디자인 선택 이지만 언어의 의미론을 따라야 할 필요는 없습니다. 실제로 +사용자 정의 유형에 과부하가 걸리면 암시 적으로 모든 것을 숫자로 변환 할 수 있습니다.

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

비록 강력한 형식의 파이썬 유형의 개체 추가 완전히 괜찮 것을 관찰 intfloat반환 형식의 개체 float(예를 들어, int(42) + float(1)반환 43.0). 반면에 Haskell 유형의 불일치로 인해 다음을 시도하면 불평 할 것 (42 :: Integer) + (1 :: Float)입니다. 이로 인해 Haskell은 엄격하게 형식화 된 언어이며 형식이 완전히 분리되어 있고 형식 클래스를 통해 제어되는 오버로드 형식 만 가능합니다.


18
자주 보지 않지만 파이썬이 완전히 입력되지 않았 음을 보여주는 것이 중요하다고 생각하는 한 가지 예는 docs.python.org/release/2.5.2/lib/truth.html입니다.
gsingh2011

25
이것이 반대 사례인지 확실하지 않은 경우 : 상황이 부울로 평가 될 수 있지만 부울로 갑자기 "되지"않습니다. 마치 누군가가 암시 적으로 as_boolean (<value>)과 같은 것을 호출하는 것처럼 객체 자체의 유형이 변경되는 것과 동일하지 않습니다.
jbrendel

15
부울 컨텍스트에서 진실하다는 것은 실제로 True또는 로 변환되는 것이 없기 때문에 반례가 아닙니다 False. 그러나 숫자 승진은 어떻습니까? 1.0 + 2비록 Perl 또는 C에서와 마찬가지로 Python에서도 잘 작동합니다 "1.0" + 2. @jbrendel에 동의합니다. 이것은 실제로 암시 적 변환이 아니라 오버로드 일뿐입니다. 그러나 같은 의미에서 Perl도 암시 적 변환을 수행하지 않습니다. 함수가 매개 변수 유형을 선언하지 않은 경우 암시 적 변환이 발생하지 않습니다.
abarnert

13
강력한 타이핑 에 대해 생각하는 더 좋은 방법 은 변수에서 작업을 수행 할 때 유형이 중요하다는 것입니다. 유형이 예상과 다를 경우, 불평하는 언어는 강력하게 입력되고 (python / java), 약하게 입력 되지 않은 언어는 ( javascript) 동적으로 입력되는 언어 (python)는 변수의 유형을 변경할 수있는 언어입니다. 반면 런타임 정적으로 입력 된 변수가 선언되면 언어 (자바)이 허용되지 않습니다.
kashif

2
@ gsingh2011 진실은 유용하고 자체적으로 약한 타이핑 은 아니지만 우발적으로 if isValid(value) - 1누출 될 수 있습니다. 부울은 정수로 강제 변환되며,이 값은 진리 값으로 평가됩니다. False - 1진실이되고 거짓 True - 1이되어 디버그하기 어려운 2 계층 오류가 발생합니다. 이런 의미에서 파이썬은 대부분 강력하게 타이핑됩니다. 유형 강제는 일반적으로 논리적 오류를 유발하지 않습니다.
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;

물론 현대 OS는 가상 메모리와 페이지 보호를 사용하므로 내 프로세스의 메모리를 덮어 쓸 수 있지만 C와 관련하여 Classic Mac OS 또는 Win16으로 코딩 한 사람이 말할 수있는 것과 같은 보호 기능을 제공하는 것은 없습니다.

전통적인 리스프는 비슷한 종류의 해커를 허용했다. 일부 플랫폼에서는 더블 워드 부동 소수점과 죄수 셀이 동일한 유형이며, 다른 하나를 기대하는 함수에 하나만 전달하면 "작동"할 수 있습니다.

오늘날 대부분의 언어는 C와 Lisp만큼 약하지는 않지만 여전히 많은 언어가 유출되어 있습니다. 예를 들어, 확인되지 않은 "다운 캐스트"가있는 OO 언어 *는 유형 누출입니다. 본질적으로 컴파일러에게 "이것이 안전하다는 것을 알 수있는 충분한 정보를주지 않았다는 것을 알고 있습니다. 형식 시스템의 요점은 컴파일러가 항상 안전한 정보를 알기에 충분한 정보를 가지고 있다는 것입니다.

* 체크 다운 캐스트는 체크를 런타임으로 옮기기 때문에 언어 유형 시스템을 더 약하게 만들지 않습니다. 그렇다면 하위 유형 다형성 (일명 가상 또는 완전 동적 함수 호출)은 유형 시스템을 위반하는 것과 같으며 아무도 그렇게 말하고 싶지 않다고 생각합니다.

이런 의미에서 "스크립팅"언어는 거의 없습니다. Perl 또는 Tcl에서도 문자열을 가져 와서 바이트를 정수로 해석 할 수는 없습니다. * CPython (및 다른 언어의 많은 다른 인터프리터의 경우)에서도 실제로 지속되는 경우 주목할 가치가 있습니다. 사용할 수있는 ctypes최대로드 libpython, 객체의 캐스팅 idA를 POINTER(Py_Object)하고, 누출 타입 시스템을 강제로. 이것이 유형 시스템을 약하게 만들지 여부는 사용 사례에 따라 다릅니다. 보안을 위해 언어 내 제한 실행 샌드 박스를 구현하려는 경우 이러한 종류의 이스케이프를 처리해야합니다.

* struct.unpack바이트를 읽는 것과 같은 함수를 사용하여 "C가 이러한 바이트를 나타내는 방법"에서 새로운 int를 빌드 할 수 있지만 분명히 누출되지는 않습니다. Haskell조차도 가능합니다.


한편, 암시 적 변환은 실제로 약하거나 새는 유형의 시스템과는 다릅니다.

Haskell조차도 모든 언어에는 정수를 문자열이나 부동 소수점으로 변환하는 기능이 있습니다. 그러나 일부 언어는 자동으로 이러한 변환 중 일부를 자동으로 수행합니다. 예를 들어 C에서 a를 원하는 함수를 호출하고이 함수를 float전달 int하면 변환됩니다. 이것은 예기치 않은 오버플로와 같은 버그로 이어질 수 있지만 약한 유형의 시스템에서 얻는 것과 같은 종류의 버그는 아닙니다. 그리고 C는 실제로 약해지지 않습니다. Haskell에 int와 float를 추가하거나 float를 문자열에 연결할 수도 있습니다. 더 명확하게 수행하면됩니다.

그리고 역동적 인 언어의 경우 이것은 매우 어둡습니다. 파이썬이나 펄에서 "float을 원하는 함수"와 같은 것은 없습니다. 그러나 유형이 다른 여러 가지 기능을 수행하는 오버로드 된 함수가 있으며, 문자열을 다른 것에 추가하는 것이 "문자열을 원하는 함수"라는 강력한 직관적 의미가 있습니다. 그런 의미에서, 펄,은 Tcl, 자바 스크립트는 암시 적 변환 (을 많이 할 표시 "a" + 1를 제공 "a1"파이썬은 많은 적은 않지만 (,) "a" + 1예외가 발생하지만, 1.0 + 1당신이 줄 않습니다 2.0*). +인덱싱과 같은 다른 기능이있을 때 문자열과 int를 사용 하는 것이 없는 이유는 무엇입니까?

* 실제로 현대 파이썬에서는 OO 하위 유형 지정으로 설명 할 수 있습니다 isinstance(2, numbers.Real). 2Perl 또는 JavaScript에서 문자열 유형의 인스턴스가 어떤 의미인지는 생각하지 않습니다 ... 비록 Tcl에서는 모든 것이 문자열의 인스턴스 이기 때문에 실제로입니다 .


마지막으로 "strong"과 "weak"입력에 대한 완전히 직교적인 또 다른 정의가 있습니다. 여기서 "strong"은 강력 / 유연성 / 표현을 의미합니다.

예를 들어 Haskell을 사용하면 숫자, 문자열,이 유형의 목록 또는 문자열에서이 유형으로의 맵인 유형을 정의 할 수 있습니다. 이는 JSON에서 디코딩 할 수있는 모든 것을 완벽하게 나타내는 방법입니다. Java에서 이러한 유형을 정의 할 방법이 없습니다. 그러나 최소한 Java에는 파라 메트릭 (일반) 유형이 있으므로 T 목록을 사용하고 요소가 T 유형임을 알고있는 함수를 작성할 수 있습니다. 초기 Java와 같은 다른 언어에서는 객체 목록과 다운 캐스트를 사용해야했습니다. 그러나 최소한 Java를 사용하면 자체 메소드를 사용하여 새 유형을 작성할 수 있습니다. C 만 구조를 만들 수 있습니다. 그리고 BCPL에는 그조차 없었습니다. 그리고 유일한 유형은 비트 길이가 다른 어셈블리입니다.

따라서 하스켈의 유형 시스템은 현대의 Java보다 강력하며, 이는 이전의 Java보다 강력하며, C보다 강력하며 BCPL보다 강력합니다.

그렇다면 파이썬은 그 스펙트럼에서 어디에 적합합니까? 조금 까다 롭습니다. 대부분의 경우 오리 타이핑을 사용하면 Haskell에서 할 수있는 모든 작업과 할 수없는 일을 시뮬레이션 할 수 있습니다. 물론 컴파일 타임 대신 런타임시 오류가 발생하지만 여전히 발생합니다. 그러나 오리 타이핑이 충분하지 않은 경우가 있습니다. 예를 들어, Haskell에서 빈 ints 목록은 int 목록이라고 말할 수 있으므로 +해당 목록 을 줄이면 0 *이 반환 되도록 결정할 수 있습니다 . 파이썬에서 빈 목록은 빈 목록입니다. 축소 할 작업을 결정하는 데 도움이되는 유형 정보 +는 없습니다.

* 사실, Haskell은 이것을 허용하지 않습니다. 빈 목록에서 시작 값을 갖지 않는 reduce 함수를 호출하면 오류가 발생합니다. 그러나 유형 시스템은 이 작업을 수행 할 있을만큼 강력하지만 Python은 그렇지 않습니다.


3
이 답변은 훌륭합니다! 그것은 목록의 맨 아래에 너무 오래 걸려서 부끄러운 일입니다.
LeoR

1
C 예제에 대한 약간의 주석 : char sz[]char에 대한 포인터가 아니며 char의 배열이며 할당에서 포인터로 부패합니다.
majkel.mk

39

'strongly typed''dynamically typed'를 혼동하고 있습니다.

1string을 추가 하여 유형을 변경할 수는 없지만 '12'변수에 저장할 유형을 선택하고 프로그램 런타임 중에 유형을 변경할 수 있습니다.

동적 타이핑의 반대는 정적 타이핑입니다. 변수 유형선언은 프로그램 수명 동안 변경되지 않습니다. 강력한 타이핑의 반대는 약한 타이핑입니다. 의 유형은 프로그램 수명 동안 변경 될 수 있습니다.


링크의 설명은 다음과 같이 강력하게 입력되었습니다. "일반적으로 강력한 형식의 언어는 컴파일 타임에 입력 규칙이 더 엄격하므로 컴파일 중에 오류와 예외가 발생할 가능성이 높습니다." 파이썬이 약한 유형의 언어임을 의미합니다 ..., 위키가 잘못 되었습니까?
비가

1
@ s̮̦̩e̝͓c̮͔̞ṛ̖̖e̬̣̦t̸͉̥̳̼ : 전혀 암시되지 않습니다. 파이썬은 컴파일 타임에 엄격한 타이핑 규칙을 가지고 있으며, 생성 된 각 객체에는 하나의 유형 만 있습니다. 그리고 '일반적으로'는 아무것도 암시하지 않으며 단지 파이썬이 예외임을 의미합니다.
Martijn Pieters

24

위키 기사 에 따르면 파이썬은 동적이고 강력하게 유형화되어 있습니다 (좋은 설명도 제공합니다).

아마도 프로그램 실행 중에 유형을 변경할 수없고 컴파일 시간 동안 유형 검사가 발생하여 가능한 오류를 감지하는 정적 유형 언어 에 대해 생각하고있을 것 입니다.

이 SO 질문은 흥미로울 수 있습니다. 동적 유형 언어와 정적 유형 언어유형 시스템 에 관한이 Wikipedia 기사 는 자세한 정보를 제공합니다.


18

TLDR;

파이썬의 타이핑은 동적 이므로 문자열 변수를 int로 변경할 수 있습니다

x = 'somestring'
x = 50

파이썬 타이핑은 강력 하므로 유형을 병합 할 수 없습니다 :

'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

이미 몇 번 대답되었지만 파이썬은 강력한 유형의 언어입니다.

>>> 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)에 따라 자동으로 한 유형에서 다른 유형으로 변환하려고합니다. 강력한 유형은 절대로 암시 적으로 변환 되지 않습니다 .

혼란은 파이썬이 값을 이름에 묶는 방법에 대한 오해에 있습니다 (일반적으로 변수라고 함).

파이썬에서는 이름에 유형이 없으므로 다음과 같은 작업을 수행 할 수 있습니다.

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://ko.wikipedia.org/wiki/Dynamic_dispatch

약간 관련이 있지만 고급입니다.

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


1
몇 년 후-또 다른 유용하고 관련된 자료 : youtu.be/_AEJHKGk9ns
Wayne Werner

강력 대 약한 입력은 3 + '4'와 같은 결과 유형의 식과 관련이 없습니다. 이 예제에서 JavaScript는 Python만큼 강력합니다.
qznc

@ qznc Javasript는 얼마나 강력합니까? 필자는 결과 유형과 관련이 있다고 생각하지 않았으며 실제로 약한 유형은 자동으로 한 유형에서 다른 유형으로 변환하려고합니다 .
Wayne Werner

2
@oneloop 반드시 그런 것은 아닙니다 .float와 int를 결합하는 동작이 잘 정의되어 있으며 float가됩니다. "3"*4파이썬에서도 할 수 있습니다 . 물론 결과는 "3333"입니다. 어느 쪽이든 변환하고 있다고 말하지 않을 것입니다. 물론 그것은 의미론을 주장하는 것일 수 있습니다.
웨인 베르너

1
그것은 파이썬이 발생하기 때문에 것을 반드시 사실이 아니다 @oneloop float의 조합 밖으로 floatint는 암시 적으로 형식을 변환 있다고. float과 int 사이에는 자연스런 관계가 있으며 실제로 계층 구조 유형이 이를 설명합니다. 파이썬 이 잘 정의 된 것과 동일한 방식으로 Javascript가 고려 '3'+4하고 'e'+4잘 정의 된 작업 이라고 주장 할 수 있다고 생각 3.0 + 4하지만, 그 시점에는 강력하거나 약한 유형과 같은 것이 없습니다. 작업.
Wayne Werner

6

Python 변수는 값을 나타내는 대상 객체에 대한 형식화되지 않은 참조를 저장합니다.

모든 할당 작업은 유형이 지정되지 않은 참조를 할당 된 객체에 할당하는 것을 의미합니다. 즉, 객체는 원본 및 새로운 (계산 된) 참조를 통해 공유됩니다.

값 유형은 참조 값이 아닌 대상 객체에 바인딩됩니다. 값이있는 작업이 수행되면 (런타임) (강한) 유형 검사가 수행됩니다.

다시 말해, 변수 (기술적으로)에는 유형이 없습니다. 변수 유형에 대해 정확하게 생각하고 싶다면 생각하는 것이 합리적이지 않습니다. 그러나 참조는 자동으로 역 참조되며 실제로 대상 객체의 유형으로 생각합니다.


6

"강력한 입력"이라는 용어에는 명확한 정의가 없습니다.

그러므로이 용어의 사용은 당신이 말하는 사람에 따라 다릅니다.

변수 유형이 명시 적으로 선언되거나 정적으로 유형이 지정되지 않은 언어는 고려하지 않습니다.

강력한 타이핑은 변환을 방해하지 않습니다 (예 : 정수에서 문자열로 "자동으로"변환). 할당을 배제합니다 (예 : 변수 유형 변경).

다음 코드가 컴파일 (해석)되면 언어가 강력하지 않습니다.

Foo = 1 Foo = "1"

강력한 형식의 언어에서 프로그래머는 형식을 "의지"할 수 있습니다.

예를 들어, 프로그래머가 선언을 보면

UINT64 kZarkCount;

그리고 20 줄 후에도 kZarkCount는 중간 코드를 검사하지 않고도 (같은 블록에서 발생하는 한) 여전히 UINT64라는 것을 알고 있습니다.


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
    }

파이썬 코드는 동적 타이핑을 보여주고 자바는 정적 타이핑을 보여줍니다. 더 좋은 예는 $ var = '2'+ 1입니다. // 결과는 3입니다
erichlf

@ivleph 동의합니다. 다음과 같이 쓸 수도 있습니다 : "a"* 3 == "aaa"
Dmitry Zagorulkin

-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....

위의 내용은 장기간에 걸쳐 큰 시스템에서 유지 관리 불가능한 코드의 악몽을 만듭니다. 원하는 것을 호출하지만 변수 유형을 "동적으로"변경하는 기능은 나쁜 생각입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.