암시 적 변환이없는 이유는 무엇입니까?


14

내가 알기로 암시 적 변환은 오류를 일으킬 수 있습니다.

그러나 그것은 의미가 없습니다. 정상적인 변환도 오류를 일으키지 않아야합니까?

왜 없어

len(100)

다음과 같이 해석하거나 컴파일하는 언어로 작업

len(str(100))

특히 그것이 그것이 작동 하는 유일한 방법 이기 때문에 . 언어는 오류가 무엇인지 알고 왜 수정하지 않습니까?

이 예제에서는 파이썬을 사용했지만이 작은 것에는 기본적으로 보편적이라고 생각합니다.


2
perl -e 'print length(100);'3을 인쇄합니다.

2
따라서 언어와 그 유형 시스템의 본질. 그것은 파이썬 디자인의 일부입니다.

2
itnesion을 모르기 때문에 수정하지 않습니다. 완전히 다른 것을하고 싶을 수도 있습니다. 루프 고소와 같지만 프로그래밍과 같은 일은 한 번도하지 않았습니다. 그래서 그것이 스스로 그것을 고치면, 사용자는 그가 틀렸거나 구현이 그가 기대하는 것을하지 않을 것이라는 것을 알지 못합니다.
Zaibis

5
@PieCrust 파이썬은 어떻게 변환해야합니까? 가능한 모든 변환이 동일한 결과를 반환하는 것은 사실 이 아닙니다 .
Bakuriu

7
@PieCrust : 문자열과 배열은 반복 가능합니다. 왜 strint를 iterable로 변환하는 암시 적 방법일까요? 방법은 range? 또는 bin( hex, oct) 또는 chr(또는 unichr)? str이 상황에서 가장 명백한 것처럼 보이지만 모든 사람들은 반복 가능 항목을 반환 합니다.
njzk2

답변:


37

무엇의 가치를 들면 len(str(100)), len(chr(100))그리고 len(hex(100))모든 다른입니다. 파이썬에서 정수에서 문자열로 하나 이상의 다른 변환이 있기 때문에 이것이 작동하는 유일한 방법 str아닙니다 . 물론 그중 하나가 가장 일반적이지만, 그것이 당신이 의미 한 것은 말할 필요도 없습니다. 암묵적인 변환은 문자 그대로 "말하지 않고 간다"를 의미합니다.

암시 적 변환의 실제 문제점 중 하나 는 몇 가지 가능성이있는 경우 어떤 변환이 적용되는지 항상 명확 하지 않다는 것입니다. 따라서 독자는 올바른 암시 적 변환을 파악하지 못하기 때문에 코드를 해석 할 때 오류가 발생합니다. 모든 사람들은 항상 그들이 의도 한 것은 "분명한 해석"이라고 말합니다. 그들이 의미하는 바이기 때문에 그들에게는 분명합니다. 다른 사람에게는 분명하지 않을 수 있습니다.

그렇기 때문에 (대부분의 경우) 파이썬은 암시적인 것보다 명시적인 것을 선호하고 위험을 감수하지 않는 것을 선호합니다. 파이썬이 강제 변환을 수행하는 주된 경우는 산술입니다. 그것은 수 있습니다 1 + 1.0대안이 살 너무 짜증나는 일 것이기 때문에,하지만 허용하지 않습니다 1 + "1"당신이 무슨 뜻인지 지정해야한다고 생각하기 때문에 int("1"), float("1"), ord("1"), str(1) + "1"다른, 또는 무언가를. 또한 결과 유형을 선택하기위한 규칙을 정의하는 (1,2,3) + [4,5,6]것처럼 결과 유형을 선택하기위한 규칙을 정의 할 있지만을 허용하지 않습니다 1 + 1.0.

다른 언어는 동의하지 않으며 많은 암시 적 변환이 있습니다. 더 많이 포함할수록 덜 명확 해집니다. 아침 식사 전에 "정수 프로모션"및 "일반적인 산술 변환"에 대한 C 표준 규칙을 기억해보십시오!


+1 len한 가지 유형으로 만 작동 할 수 있는 시작 가정 이 근본적으로 결함이 있음을 보여줍니다 .
KChaloux

2
@KChaloux : 실제로, 제 예제 str에서는 chr, hex모두 같은 타입을 반환합니다! 생성자가 int를 사용할 수있는 다른 유형을 생각하려고했지만 아직 아무것도 찾지 못했습니다. ;-) 가 약간 애매 모호하게 보이는 컨테이너 X가 이상적입니다 . len(X(100)) == 100numpy.zeros
Steve Jessop

2
가능한 문제의 예는 다음을 참조하십시오. docs.google.com/document/d/…destroyallsoftware.com/talks/wat
Jens Schauder

1
묵시적 변환 (이것은 강압이라고합니다)은 a == b, 와 같은 불쾌한 것들을 야기 할 수 b == c있지만 a == c사실이 아닙니다.
bgusach

훌륭한 답변입니다. 내가 말하고 싶은 모든 것, 더 나은 말로만! 내 유일한 사소한 문제는 C 정수 프로모션이 JavaScript 강제 규칙을 암기하거나 암시 적 생성자를 부주의하게 사용하여 C ++ 코드베이스를 감싸는 것에 비해 매우 간단하다는 것입니다.
GrandOpener 2018 년

28

내가 알기로 암시 적 변환은 오류를 일으킬 수 있습니다.

단어가 누락되었습니다. 암시 적 변환으로 인해 런타임 오류 가 발생할 수 있습니다 .

당신이 보여주는 것과 같은 간단한 경우에, 당신이 의미하는 것이 분명합니다. 그러나 언어는 사례에서 작동하지 않습니다. 규칙을 다루어야합니다. 다른 많은 상황에서는 프로그래머가 잘못된 유형을 사용하여 오류가 발생했는지 또는 프로그래머가 코드에서 의도 한대로 수행하려고했는지 명확하지 않습니다.

코드가 잘못되었다고 가정하면 런타임 오류가 발생합니다. 추적하기가 지루하기 때문에 많은 언어가 엉망이고 컴퓨터가 실제로 무엇을 의미하는지 (버그 수정 또는 명시 적으로 변환) 컴퓨터에 알려주는 측면으로 잘못되었습니다. 다른 언어는 그 스타일이 디버그하기 쉬운 빠르고 쉬운 코드에 적합하기 때문에 추측을합니다.

한 가지 주목할 점은 암시 적 변환으로 인해 컴파일러가 좀 더 복잡해집니다. 당신은 사이클에 대한 자세한 조심해야 (의는 A로부터 B에이 변환을 시도하자;! 작업 그렇지 않은 죄송하지만,이 변환은 B에서 A의 와 C와 D가 너무주기를 크기에 대해 다음 걱정할 필요 ), 어떤 암묵적인 전환을 피하려는 작은 동기입니다.


대부분 한 유형에서만 작동 할 수있는 함수에 대해 이야기하고 있었으므로 변환이 명확 해졌습니다. 여러 유형에서 작동하는 유형은 무엇이며 입력 한 유형이 차이를 만듭니다. print ( "295") = print (295)이므로 변수를 제외하고는 차이가 없습니다. 3d 단락을 제외하고 말한 내용은 ... 반복 할 수 있습니까?
Quelklef

30
@PieCrust-123 + "456", "123456"또는 579를 원하십니까? 프로그래밍 언어는 컨텍스트를 수행하지 않으므로 추가가 수행되는 컨텍스트를 알아야하기 때문에 "그림을 작성하기"가 어렵습니다. 어느 단락이 명확하지 않습니까?
Telastyn

1
또한 10 진수로 해석하는 것이 원하는 것이 아닐 수도 있습니다. 그렇습니다. 암시 적 변환은 좋은 일 이지만 오류를 숨길 수없는 경우에만 가능합니다.
중복 제거기

13
@PieCrust : 솔직히 말해서 나는 결코 len(100)돌아올 것으로 기대하지 않을 것이다 3. 의 표현에서 비트 수 (또는 바이트)를 계산하는 것이 훨씬 직관적이라는 것을 알았습니다 100.
user541686

3
내가 @Mehrdad와 함께 해요, 당신의 예에서는 것은 분명 당신 은 그 100 % 분명하다 생각하는 len(100)당신에게 수 (100)의 진수 표현의 문자의 양을 제공해야하지만 그건 모르고하는 동안있는 거 결정 실제로 가정의 톤입니다 그들의. 16 진수 표현 ( 64-> 2 문자) 의 비트 또는 바이트 또는 문자 수를로 반환해야하는 이유를 강력하게 주장 할 수 있습니다 len().
funkwurm

14

암시 적 변환이 가능합니다. 문제가 발생하는 상황은 어떤 방식으로 작동해야하는지 모르는 경우입니다.

이 예제는 +운영자가 다른 시간에 다른 방식으로 작업 하는 Javascript에서 볼 수 있습니다 .

>>> 4 + 3
7
>>> "4" + 3
43
>>> 4 + "3"
43

인수 중 하나가 문자열 인 경우 +연산자는 문자열 연결이며 그렇지 않은 경우 추가입니다.

인수가 주어졌고 그것이 문자열인지 정수인지 모르고 그것을 추가하고 싶다면 약간 혼란 스러울 수 있습니다.

이것을 다루는 또 다른 방법은 기본 유산에서 온 것입니다 (펄은 다음과 같습니다- 프로그래밍은 어렵다, 스크립팅은 가십시오 ... ).

Basic에서는 len함수가 String에서 호출되는 것이 의미가 있습니다 ( Visual Basic의 경우 문서 : "모든 유효한 문자열 표현식 또는 변수 이름. Expression이 Object 유형 인 경우 Len 함수는 파일에 기록 될 크기를 반환합니다. FilePut 기능. ").

Perl은 이러한 맥락 개념을 따릅니다. 에 대한 유형의 암시 적 변환과 자바 스크립트에 존재하는 혼란 +운영자가 때때로 추가되고, 때로는 연결이 펄에서 발생하지 않습니다이 있기 때문 +입니다 항상 추가 및 .입니다 항상 연결.

스칼라 컨텍스트에서 스칼라로 사용되는 항목 (예 : 목록을 스칼라로 사용하는 경우 목록은 해당 길이에 해당하는 숫자 인 것처럼 동작 함) 문자열 연산자 ( eq등식 테스트, cmp문자열 비교)를 사용하는 경우 스칼라는 문자열 인 것처럼 사용됩니다. 마찬가지로, 수학 문맥에서 어떤 것이 ( ==등식 테스트 및 <=>숫자 비교를 위해) 사용 된 경우 스칼라는 숫자 인 것처럼 사용됩니다.

모든 프로그래밍의 기본 규칙은 "사람을 가장 놀라게하는 것"입니다. 그렇다고해서 놀라움이 없다는 것을 의미하는 것은 아니지만 그 노력은 사람을 가장 놀라게하는 것입니다.

perl-php의 가까운 사촌으로 가면 연산자가 문자열이나 숫자 컨텍스트에서 무언가에 대해 행동 할 수 있고 사람들에게 그 행동이 놀라운 상황이 있습니다. ++작업자는 하나의 이러한 예이다. 숫자에서는 예상대로 정확하게 동작합니다. 과 같은 문자열에 작용할 때 문자열 "aa"을 증가시킵니다 ( $foo = "aa"; $foo++; echo $foo;prints ab). 또한 az증분 될 때가 되도록 롤오버 됩니다 ba. 이것은 아직 놀라운 일이 아닙니다.

$foo = "3d8";
echo "$foo\n";
$foo++;
echo "$foo\n";
$foo++;
echo "$foo\n";
$foo++;
echo "$foo\n";

( 아이디어 )

인쇄됩니다 :

3d8
3d9
3e0
4

암시 적 변환 및 연산자가 동일한 문자열에서 다르게 작동하는 위험에 오신 것을 환영합니다. (Perl은 코드 블록을 조금 다르게 처리합니다.- 연산자가 적용될 "3d8"++시작부터 숫자 값으로 결정하고 4바로 가기 ( ideone )-이 동작은 perlop에 잘 설명되어 있습니다 : 자동 증가 및 자동 감소 )

이제 왜 한 언어가 어떤 방식으로 무언가를하고 다른 언어가 다른 방식으로 디자이너의 디자인 사고에 도달 하는가? Perl의 철학은 하나 이상의 방법이 있다는 것입니다. 저는 이러한 작업을 수행하는 여러 가지 방법을 생각할 수 있습니다. 다른 한편으로, 파이썬은 PEP 20-파이썬의 선 (Phen of Zen of Python)에 기술 된 철학을 가지고 있습니다. "그것을하는 명백한 방법이 있어야합니다.

이러한 디자인 차이로 인해 다른 언어가 사용되었습니다. 파이썬에서 숫자의 길이를 얻는 한 가지 방법이 있습니다. 암묵적인 전환은이 철학에 위배됩니다.

관련 자료 : Ruby에서 Fixnum을 문자열로 암시 적으로 변환하지 않는 이유는 무엇입니까?


IMHO, 좋은 언어 / 프레임 워크는 종종 일반적인 코너 케이스를 다르게 처리하는 여러 가지 방법을 가져야합니다 (1000 개의 프로그램에서 1000 회보다 언어 나 프레임 워크에서 일반적인 코너 케이스를 한 번 처리하는 것이 더 낫습니다). 두 연산자가 대부분 동일한 시간을 수행한다는 사실은 그들의 행동이 다를 때 각 변형에 이점이 있다면 나쁜 것으로 간주해서는 안됩니다. 두 개의 숫자 변수 비교하기 어려울 안 xy순위 동치 관계 또는를 산출하는 것과 같은 방식으로, 그러나 IIRC 파이썬의 비교 연산자 ...
supercat

... 등가 관계 나 순위를 구현하지 않으며 Python은 편리한 연산자를 제공하지 않습니다.
supercat

12

ColdFusion이이 대부분을 수행했습니다. 암시 적 변환을 처리하기위한 일련의 규칙을 정의하고 단일 변수 유형을 가지고 있습니다.

결과는 총 무정부 상태이며, "4a"를 6에 더하면 6.16667입니다.

왜? 두 변수 중 첫 번째 변수는 숫자이므로 결과는 숫자입니다. "4a"는 날짜로 구문 분석되며 "4 AM"으로 표시됩니다. 오전 4시는 4 : 00/24 ​​: 00 또는 하루의 1/6입니다 (0.16667). 6에 더하면 6.16667이됩니다.

목록에는 쉼표의 기본 구분 문자가 있으므로 쉼표가 포함 된 목록에 항목을 추가 한 경우 두 항목 만 추가했습니다. 또한 목록은 비밀리에 문자열이므로 하나의 항목 만 포함하면 날짜로 구문 분석 할 수 있습니다.

문자열 비교는 두 문자열을 날짜로 먼저 구문 분석 할 수 있는지 확인합니다. 날짜 유형이 없으므로 그렇게합니다. 숫자, 8 진 표기법 및 부울 ( "true"및 "yes"등)을 포함하는 숫자 및 문자열에 대해서도 동일합니다.

ColdFusion은 빠른 실패와 오류보고 대신 데이터 유형 변환을 처리합니다. 그리고 좋은 방법은 아닙니다.

물론 DateCompare와 같은 명시 적 함수를 호출하여 암시 적 변환을 수정할 수 있지만 암시 적 변환의 "혜택"을 잃어 버렸습니다.


ColdFusion의 경우 개발자에게 권한을 부여하는 데 도움이됩니다. 특히 모든 개발자가 HTML에 익숙한 경우 (ColdFusion은 태그와 함께 작동하며 CFML이라고하며 나중에 <cfscript>모든 것을 위해 태그를 추가 할 필요가없는 스크립팅 을 추가했습니다). 그리고 무언가를 원할 때 잘 작동합니다. 그러나 좀 더 정확한 방식으로 일을해야하는 경우 실수 였을 가능성이있는 것처럼 보이는 것을 암시 적으로 변환하지 않는 언어가 필요합니다.


1
와우, "총 무정부 상태"!
hoosierEE

7

당신은 암시 적 변환이 같은 모호 작업에 대한 좋은 생각이 될 수 있다는 말을하는지 int a = 100; len(a)당신은 분명히 평균 호출하기 전에 문자열로 INT 변환.

하지만 이러한 호출 될 수 있다는 것을 잊고있는 구문 모호하지만, 그들은 전달하는 의미 프로그래머에 의해 만들어진 오타 나타낼 수 a1있는 것입니다 문자열을. 이것은 고안된 예이지만 변수 이름에 자동 완성 기능을 제공하는 IDE를 사용하면 이러한 실수가 발생합니다.

유형 시스템은 오류를 피하는 데 도움이되고보다 엄격한 유형 검사를 선택하는 언어의 경우 암시 적 변환이이를 손상시킵니다.

==에 의해 수행되는 모든 암시 적 변환으로 Javascript의 문제를 확인하여 많은 사람들이 no-implicit-conversion === 연산자를 고수하는 것이 좋습니다.


6

당신의 진술의 맥락을 잠시 고려하십시오. 이것이 "유일한 방법"이라고 말하지만 실제로 그렇게 작동 할 것이라고 확신하십니까? 이건 어때?

def approx_log_10(s):
    return len(s)
print approx_log_10(3.5)  # "3" is probably not what I'm expecting here...

다른 사람들이 언급했듯이, 매우 구체적인 예에서 컴파일러가 의미를 직관적으로 표현하는 것이 간단 해 보입니다. 그러나 더 복잡한 프로그램의 더 큰 맥락에서 문자열을 입력하거나 다른 변수 이름을 입력하거나 잘못된 함수 또는 수십 가지 다른 잠재적 논리 오류를 입력할지 여부는 종종 분명하지 않습니다. .

인터프리터 / 컴파일러가 의미하는 바를 추측하는 경우 때로는 마술처럼 작동하며, 다른 경우에는 별다른 이유없이 신비하게 작동하지 않는 무언가를 디버깅하는 데 몇 시간을 소비합니다. 평균적으로, 진술이 이해가되지 않는다는 말을 듣는 것이 훨씬 안전하며, 실제로 의도 한 바에 맞게 수정하는 데 몇 초가 소요됩니다.


3

암시 적 변환은 작업하기가 매우 어려울 수 있습니다. PowerShell에서 :

$a = $(dir *.xml) # typeof a is a list, because there are two XML files in the folder.
$a = $(dir *.xml) # typeof a is a string, because there is one XML file in the folder.

암시 적 변환이없는 것보다 두 배나 많은 테스트가 필요하고 두 배나 많은 버그가 있습니다.


2

명백한 캐스트는 의도를 명확하게하기 위해 중요합니다. 우선 명시 적 캐스트를 사용하면 코드를 읽는 사람에게 이야기를 들려줍니다. 그들은 당신이 의도적으로 당신이 한 일을했음을 보여줍니다. 또한 컴파일러에도 동일하게 적용됩니다. 다음은 C #에서 잘못된 예입니다.

double d = 3.1415926;
// code elided
int i = d;

캐스트는 정밀도를 떨어 뜨려 쉽게 버그가 될 수 있습니다. 따라서 컴파일러는 컴파일을 거부합니다. 명시 적 캐스트를 사용하면 컴파일러에게 "내가하는 일을 알고 있습니다." 그리고 그는 갈 것이다 : "좋아!" 그리고 컴파일하십시오.


1
실제로, 나는 암시적인 것보다 대부분의 명백한 캐스트를 싫어한다. IMHO의 유일한 (X)y의미는 "Y는 X로 변환 가능하다고 생각합니다. 가능하면 변환을 수행하거나 그렇지 않으면 예외를 발생시킵니다"; 변환이 성공하면 type에서 type으로 변환하는 데 대한 특별한 규칙을 몰라도 결과 값이 무엇인지 예측할 수 있어야 y합니다 X. -1.5 및 1.5를 정수로 변환하도록 요청하면 일부 언어는 (-2,1)을 생성합니다. 일부 (-2,2), 일부 (-1,1) 및 일부 (-1,2). C의 규칙은 잘 모호하지 않지만 ...
supercat

1
... 그 종류의 변환은 캐스트보다 메소드를 통해보다 적절하게 수행되는 것처럼 보입니다 (너무 나쁜 .NET에는 효율적인 라운드 앤 변환 기능이 포함되어 있지 않으며 Java는 다소 어리석은 방식으로 오버로드됩니다).
supercat

컴파일러에게 " 내가하는 일을 알고 있다고 생각한다 " 말하고 있다
gnasher729

@ gnasher729 진실이 있습니다 :)
Paul Kertscher

1

암시 적 변환 / 캐스트 또는 때때로 언급되는 약한 타이핑을 지원하는 언어는 의도하거나 예상 한 동작과 항상 일치하지 않는 것으로 가정합니다. 언어 디자이너는 특정 유형의 변환이나 일련의 변환에 대해 생각한 것과 동일한 사고 과정을 겪었을 수 있으며,이 경우 문제가 발생하지 않습니다. 그러나 그렇지 않은 경우 프로그램이 실패합니다.

그러나 실패는 명백하거나 명백하지 않을 수 있습니다. 이러한 암시 적 캐스트는 런타임에 발생하기 때문에 프로그램이 행복하게 실행되며 프로그램의 출력 또는 결과를 볼 때만 문제가 나타납니다. 명시 적 캐스트가 필요한 언어 (일부는 강력한 타이핑이라고 함)는 프로그램이 실행을 시작하기 전에 오류를 발생시킬 수 있습니다. 이는 암시 적 캐스트가 잘못되었다는 것을 해결하는 훨씬 더 분명하고 쉬운 문제입니다.

얼마 전 내 친구가 2 살짜리에게 20 + 20이 무엇인지 물었고 2020 년에 대답했다. 나는 친구에게 "그는 자바 스크립트 프로그래머가 될 것"이라고 말했다.

자바 스크립트에서 :

20+20
40

20+"20"
"2020"

따라서 암시 적 캐스트로 인해 발생할 수있는 문제와 수정이 불가능한 이유를 알 수 있습니다. 내가 주장하는 해결책은 명시 적 캐스트를 사용하는 것입니다.

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