코드를 머지하려고 보면 참 기분 좋은 경우가 있는가 하면, "이게 뭐지?!" 하는 생각이 절로 드는 경우도 있다. 내가 코드를 보면서 초보와 초보가 아님을 가르는 가장 큰 기준 중 하나가 바로 '가시성(Visibility)', 즉 '스코프(Scope)'다.
스코프란 프로그램 전체에서 해당 변수나 함수가 어디까지 노출될지를 결정하는 거라고 보면 된다. 이런 설정이 잘 된 코드는 일단 기본기가 탄탄하다는 느낌을 주지만, 이게 엉망인 코드는 보는 내내 불안감과 의문을 던져준다.
#0
요즘 많이 쓰는 파이썬 코드로 시작해보자.
global_variable = 1 # 1. 전역 변수
class OneBigBeautifulClass:
class_variable = 2 # 2. 클래스 변수
def __init__(self):
self.member_variable = 3 # 3. 인스턴스 변수
def plus(self, a, b):
local_variable = a + b # 4. 지역 변수
return local_variable
여기엔 크게 4가지 변수가 있다. global_variable
, class_variable
, member_variable
, local_variable
이 그것이다. 순서대로 스코프 범위가 넓다.
- 전역 변수: 이 코드 전체, 어디서든 참조할 수 있다.
- 클래스 변수: 인스턴스와 관계없이 해당 클래스 전체에서 참조된다.
- 인스턴스 변수: 생성된 인스턴스로 범위가 제한된다. (
self.
) - 지역 변수:
plus
함수 내에서만 목숨이 붙어있다.
#1
이 코드를 보면 간단한 원리를 발견할 수 있다. 대체로 넓은 범위의 변수는 좁은 범위의 변수 기능을 대체할 수 있다는 점이다. 즉, 전역 변수로 지역 변수가 할 일을 시켜도 코드는 일단 돌아간다.
# [예시 1] 일단은 문제 없이 동작한다
global_variable2 = 0
class OneBigBeautifulClass:
def plus(self, a, b):
global global_variable2
global_variable2 = a + b
return global_variable2
하지만 그 반대는 안 된다. 지역 변수가 전역 변수를 대체할 수는 없다.
# [예시 2] 당연히 동작하지 않는다
class OneBigBeautifulClass:
def plus(self, a, b):
local_variable = a + b
return local_variable
print(local_variable) # 에러 발생
넓은 범위로 확장이 가능하기에, 초보 코더가 작성한 코드를 보면 필요 이상으로 스코프를 크게 잡는 문제가 생긴다. 지역 변수로 써도 될 것을 굳이 멤버 변수로, 클래스 변수로, 심지어 전역 변수로 쓰는 것이다.
"돌아가면 장땡 아니냐?" 할 수 있다. 하지만 변수든 함수든 범위가 넓다는 건 그만큼 조심해야 할 게 많다는 의미다. 집 안에서는 알몸으로 돌아다녀도 뭐라 할 사람이 없지만, 집 밖으로 나올 때는 주의해야 하는 거랑 같은 원리다. 스코프가 넓은 변수는 여기저기서 의도치 않게 건드려질 수 있는 위험에 항상 노출된다.
#2
초보 티를 벗기 위해 명심해야 할 한 가지는 최소 가시성 원칙이다. 내가 만들 변수나 함수의 가시성은 '최소한'이 되도록 설정하면 대체로 그게 맞다는 말이다.
전역 변수를 썼는데 생각해보니 지역 변수로도 될 것 같다? 그럼 지역 변수로 바꾸는 게 맞다. 멤버 변수(self.변수
)로 썼는데, 한 함수 안에서만 쓰고 만다? 그럼 지역 변수로 하는 게 맞다.
아래 코드를 보자. self.member_variable
은 오직 plus
함수 안에서만 사용된다.
class OneBigBeautifulClass:
def __init__(self):
self.member_variable = 0
def plus(self, a, b):
self.member_variable = a + b
return self.member_variable
이럴 땐 굳이 인스턴스 전체가 공유하는 멤버 변수로 둘 필요가 없다. 아래처럼 바꾸는 것이 훨씬 안정적이고 좋은 코드다.
# [더 좋은 코드]
class OneBigBeautifulClass:
def __init__(self):
pass
def plus(self, a, b):
local_variable = a + b # plus 함수에서만 쓸 변수이므로 지역 변수로
return local_variable
이처럼 변수의 스코프를 필요한 만큼만 최소한으로 부여하는 습관, 이것만으로도 코드의 품질은 훨씬 좋아진다.