Python

Python의 단점

Falto 2024. 10. 3. 17:53
반응형

1. Static typing을 할 수 없다.

def f(x:int)->int:
    return x+1
f("asdf")

나한테 파이썬의 가장 큰 단점을 꼽으라고 한다면 단연 dynamic typing이다. 위 코드에서 함수 f는 정수 x를 입력으로 받아 x+1을 반환하는 함수이다. 그런데 여기에 문자열 asdf를 넣는 코드를 작성했다. 컴파일 언어라면 컴파일 자체가 안 되겠지만, Python은 인터프리터 언어다. 따라서, 소스 코드를 꼼꼼히 읽어보지 않는 이상 f("asdf")라는 얼토당토않은 코드를 작성해도 코드를 직접 실행해서 TypeError를 뿜는 것을 보기 전까지는 코드가 잘못되었다는 것을 알 수가 없다. 그리고 프로그램에 분기점이 많아지면, 단위 테스트를 작성하지 않는 이상 모든 코드를 다 실행해보는 것은 불가능에 가깝다. 그 말인즉슨 버그를 잡아내기가 굉장히 어렵다는 것이다. 왜 TypeScript는 있는데 TypePython은 없는 걸까...

 

2. for문, if문, while문에서 쓰이는 변수가 외부에도 노출된다.

for i in range(2):print(i)
print(i)

위 코드를 실행시켜보면 0 1 1이 나온다. 나는 for문 안에서만 i를 쓰고 싶은데 for문 밖에서도 i를 쓸 수 있게 되어버린다. 이걸 방지하려면 for문이 끝나자마자 del i를 써서 변수를 제거해줘야 하는데 그건 귀찮을 뿐만 아니라 코드도 지저분하게 만든다.

if 1:
 x=2
print(x)

이렇게 if문 안에서 선언된 변수도 if문 밖에서 접근할 수 있다.

사실 del로 변수를 제거하는 것은 귀찮고 지저분한 것뿐만 아니라 NameError까지 불러일으킬 수 있다.

for x in range(0):
 ...
 
while 0:
 x=0

if 0:
 x=0

del x # NameError: name 'x' is not defined

위 코드에서 x 선언문은 for문, while문, if문 중 어디에서도 실행되지 않는다. 따라서 del x에서 NameError가 난다. 이걸 방지하려면 del을 일일히 try: except NameError: 로 감싸줘야 한다.

3. circular import 때문에 오류가 난다.

tmp1.py

import tmp2
class Apple(tmp2.Banana):...

 

tmp2.py

import tmp1
def f()->tmp1.Apple:
    return tmp1.Apple()
class Banana:...

두 파이썬 코드는 모두 서로 다른 모듈의 클래스를 끌어다 쓰고 있다.

 

tmp1.py, tmp2.py를 실행시켜보면 각각 아래와 같이 오류가 출력된다.

    class Apple(tmp2.Banana):...
                ^^^^^^^^^^^
AttributeError: partially initialized module 'tmp2' has no attribute 'Banana' (most likely due to a circular import)

 

    def f()->tmp1.Apple:
             ^^^^^^^^^^
AttributeError: partially initialized module 'tmp1' has no attribute 'Apple' (most likely due to a circular import)

 

이러한 오류 때문에 파일 하나에 class 하나를 넣어서 유지보수하기가 어렵다. 위와 같은 상황일 경우 tmp2.py를 실행하고 싶을 경우 어쩔 수 없이 type annotation을 포기해야 한다.

4. 들여쓰기만으로 코드의 실행 결과가 바뀐다.

if 1:
 print(2)
else:
 print(3)
print(4)

위 코드에서 print(4)의 앞 부분에 공백 한 칸만 넣어도 4는 출력되지 않는다. 중괄호로 블록을 구분하는 C, C++, C#, Java 등의 언어와는 다르게 Python은 들여쓰기가 아니면 블록을 구분할 수가 없다. 코드 복사 붙여넣기 잘못하다가 들여쓰기가 사라져버리면 완전히 다른 코드가 탄생해버릴 수도 있다.

5. 객체 지향 프로그래밍을 불편하게 만드는 __ 접두사

class Person:
    def __init__(self):
        self.__age = 10
print(Person().__age) # AttributeError: 'Person' object has no attribute '__age'

Python에선 private을 나타내기 위해 변수명 앞에 __를 붙여야 한다. 그리고 앞으로 있을 모든 클래스 메소드들 내부에서 죄다 __를 앞에 붙인 그 변수를 써야 한다. 객체 지향 프로그래밍의 달성 목표 중 하나가 정보 은닉인데, 이거 하나를 위해서 private 변수를 쓸 때마다 underscore 2개씩 붙여야 한다. 여간 귀찮은 일이 아니다. Python 처음 독학한 사람들은 객체 지향 프로그래밍의 장점이 뭔지도 모른 체 underscore 없이 죄다 public 변수를 생성하게 될 수도 있다. 처음부터 객체 지향과 멀어지는 습관을 유도하는 것이다.

6. 함수 오버로딩을 지원하지 않는다.

def f(x):
    print(x)
def f(x,y):
    print(x,y)
f(3) # TypeError: f() missing 1 required positional argument: 'y'

함수 오버로딩을 지원하는 C#, C++ 같은 언어들은 다른 시그니쳐에 대해 같은 이름의 함수를 선언할 수 있다. 근데 이런 이름이 똑같은 함수를 파이썬에 포팅하려면 선택 인수를 넣든 함수 이름을 바꾸든 해야 한다. 함수 오버로딩이 안 되기 때문에 생성자 오버로딩도 안 된다.

 

반응형

'Python' 카테고리의 다른 글

시간 복잡도와 이중 for문  (0) 2021.06.04
while문에서 time.sleep의 필요성  (0) 2021.05.17