예전에도 할당한적 없는 값이 객체에 멤버 변수에 계속 할당되는 이슈가 있었는데, 오랫동안 잊고지내다 또 발생해서..
원인 찾는데 삽질 시~원하게 해주고 다시는 반복하지 않기 위해서 포스팅까지 작성해봅니다
문제 배경
fastapi 앱을 TestClient & 멀티 쓰레드 환경에서 테스트하던 중 계속 DB primary 중복 에러가 발생했다..
조금 트랙킹을 해보니 서로 다른 http scope간 데이터가 엉키는 현상이 발견됬다.
여러 Session 클래스와 멀티 쓰레드 환경이다보니 어디부터 잘못된건지..
하나씩 데이터를 추적하면서 좁혀가려고하는데 엉키는 시점이 매번 다르고 착잡하던 와중에 아무래도 Session 소켓 내에서 데이터가 꼬이는 현상은 절대 아닐거 같고 (설명에도 TestClient는 thread 환경에서도 사용할 수 있다는 문구가 있었다..!!)
분명 클래스, 객체 사용법이 잘못된 것 같다고 판단했다
보통 파이썬은 private 변수를 잘 사용하는 언어는 아니지만, 클래스의 캡슐레이션을 강조하고 싶은 사유가 있어서 사용했는데 이걸 잘못 썻나.. 이상한 의심까지하던 찰나! 초기화 함수의 default 변수가 눈에 들어왔다
무심코 작성한 default 변수때문에 2시간동안 뭘한건지 ㅠㅠ
문제 원인
문제 원인을 바로 꺼내자면, default 매개변수로 Mutable한 객체를 사용한 것이다.
항상 python3 IDE나 pydantic 등에서 default 매개변수로 그렇게 Mutable 객체 쓰지 말라고 경고 띄우던게 이런 이유였군요 😂
과거에도 한번 데였던 문제를 금붕어마냥 또 당했는데 다음엔 실수하지 않겠지요..
문제가 발생하게된 과정을 간략하게 설명하자면, (Mutable, Immutable이 무엇인지 설명은 생략하겠다)
파이썬은 기본매개변수 또한 실행시간에 instance화되는 객체중 하나이다.
아래 그림처럼 Foo() 인스턴스가 생성되고 해당 인스턴스가 매개변수로 사용되는 것이다.
아래 케이스를 보면 왜 Mutable한 객체를 기본 매개변수로 절대 사용하면 안되는지 쉽게 알 것이다.
기본 매개변수를 사용한다는건 사실상 같은 instance의 메모리 주소를 참조한다는 것과 같은 것이다.
Immutable 개체들이야 변동이 없으니 큰 문제 없지만, Mutable의 경우 다른 누군가가 내부값을 변경해버리면, 동일한 기본 변수를 사용했던 또 다른 개체들에게 예상하기 어려운 영향을 전파해버린다.
class Foo:
def __init__(a=0, b=0):
self.a = a
self.b = b
class Bar:
def __init__(foo: Foo = Foo()):
self.foo = foo
bar1 = Bar() # foo.a=0, foo.b=0
bar2 = Bar() # foo.a=0, foo.b=0
bar1.foo.a = 3
print(bar2.foo.a)
# => 3
print(id(bar1.foo) == id(bar2.foo))
# => True
pydantic BaseModel처럼 알아서 잘 처리해주는 케이스도 있긴하다.
그럼에도 pycharm IDE에서는 pydantic에서도 웬만하면 저렇게 사용하지 말고 default_factory를 사용하라고 제안한다.
>>> from pydantic import BaseModel
>>>
>>> class Foo(BaseModel):
... a: list[int] = []
...
>>>
>>> foo1 = Foo()
>>> foo2 = Foo()
>>>
>>> foo1
Foo(a=[])
>>> foo2
Foo(a=[])
>>> foo1.a.append(3)
>>> foo1
Foo(a=[3])
>>> foo2
Foo(a=[])
'Python' 카테고리의 다른 글
pyinstaller, bootloader는 무엇인가? (0) | 2023.01.24 |
---|---|
Ubuntu(우분투) Jupyter notebook 원격 접속 (0) | 2020.05.31 |
Ubuntu(우분투) Jupyter notebook 파이썬 가상환경의 커널 추가 (2) | 2019.12.30 |
ubuntu(우분투) Pycharm 설치 및 사용법, 가상환경 생성 (0) | 2019.12.29 |
ubuntu(우분투) python virtualenv 가상환경 생성 (0) | 2019.12.29 |
댓글