먼저 여러분이 클래스 바디 스코프(class body scope)에 대한 개념을 잘 알고 있는지 테스트 해보겠습니다.!
아래의 코드를 보시고 어떤 값이 출력 될것 같은가요?
MAJOR = 0
MINOR = 0
REVISION = 1
def gen_class():
MAJOR = 0
MINOR = 4
REVISION = 2
class Language:
MAJOR = 3
MINOR = 7
REVISION = 4
def version(self):
return '{}.{}.{}'.format(MAJOR, MINOR, REVISION)
return Language() # 객체 생성
lang = gen_class()
lang.version()
정답은...
0.4.2 입니다.
위 문제를 틀리신 분들은 끝까지 이번 내용을 정독해주시길 바랍니다!
위 코드는 함수안에 클래스를 집어 넣은 형태로.,
함수 version 안에 있는 MAJOR, MINOR, REVISION가 어디서 값을 가져오는지에 대한 문제입니다.
일반적으로 당연히 클래스 안에 있는 함수니까. "가장 가까이에있는 클래스안에서 정의된 값들을 가져오지 않을까?" 하고 오해를 많이 하죠.
클래스가 없는 단순 함수에선 그게 맞았지만 안타깝게도 클래스에선 다릅니다!
클래스에선 가장 가까이있는 enclosing scope에서 값을 가져옵니다.
다시말하면 클래스 안이 아닌 그 밖 어딘가에서 값을 가져오는데 여기선 gen_class()내에 있는 값들이 클래스 밖에선 제일 가장 가까운 값이기 때문에 가져온것이지요.
그렇다면 이 코드에서 어떤 값을 출력될까요?
MAJOR = 0
MINOR = 0
REVISION = 1
def gen_class():
class Language:
MAJOR = 3
MINOR = 7
REVISION = 4
def version(self):
return '{}.{}.{}'.format(MAJOR, MINOR, REVISION)
return Language() # 객체 생성
lang = gen_class()
lang.version()
답은...
0.0.1 입니다.
앞선 예제와 다르게 여기선 gen_class()내에 MAJOR, MINOR, REVISION 이 없기때문에 더 상위 차원으로 가서 값을 찾게되는데, 마침 module(global scope)에서 정의된 MAJOR, MINOR, REVISON 값이 있으니 여기서 가져오게 되는 것입니다.
자 이제 어느 정도 감을 잡으셨으니 다음 문제를 풀어볼까요?
name = 'Guido'
class MyClass:
name = 'Raymond'
list_1 = [name] * 3
print(MyClass.list_1)
class밖에서 값을 가져올테니 Guido를 가져와서
['Guido', 'Guido', 'Guido']가 아닐까라고 예상하신분들도 있을텐데요..
정답은...
['Raymond', 'Raymond', 'Raymond']
입니다.
클래스밖 scope에 있는 'Guido'를 가져오지 않고 이번에는 클래스 내에 있는 name = 'Raymond'를 가져왔네요..
이게 무슨일일까요..?
여기서 중요한 차이점을 알 수 있습니다.
앞선 예들은 모두 클래스내에 지정된 함수였다면 list_1은 클래스에서 내에서 지정된 property값입니다.
즉 property내에서 변수값을 가져올때는 클래스 바디내에 있는 값들을 우선 체크하는 것이죠.
여기선 name = 'Raymond'가 클래스 내에 있으니 값을 가져오는 것입니다.
내가 함수를 쓰고싶은데, 클래스내 변수를 이용하고 싶다면..
우리가 일반적으로 자주보는 self.와 같은 형태로 코드진행이 됩니다.
class Language:
MAJOR = 3
MINOR = 7
REVISION = 4
def version(self):
return '{}.{}.{}'.format(self.MAJOR, self.MINOR, self.REVISION)
@classmethod # 객체를 생성하면 디폴트로 첫번째 인자위치에 객체가 들어오는데 클래스가 인자로 들어오게 하기 위함.
def cls_version(cls):
return '{}.{}.{}'.format(cls.MAJOR, cls.MINOR, cls.REVISION)
@staticmethod # 객체를 생성하면 디폴트로 첫번째 인자위치에 객체가 들어오기때문에 아무것도 안들어오게 하기위함
def static_version():
return '{}.{}.{}'.format(Language.MAJOR, Language.MINOR, Language.REVISION)
l = Language() # 객체 생성
print(l.version())
print(l.cls_version())
print(l.static_version())
보시는것처럼 그냥 MAJOR, MINOR, REVISION 을 그냥 적는게 아니라.
각 함수 특성에 맞게 self 나 cls 또는 클래스명을 앞에 붙혀줌으로써 클래스안에 정의된 값들을 쓸수 있게 됩니다.
output:
이 밖에도 클래스 밖에서 함수를 정의 하고 사용 할 수도 있는데요.
def full_version():
return '{}.{}.{}'.format(Language.MAJOR, Language.MINOR, Language.REVISION)
class Language:
MAJOR = 3
MINOR = 7
REVISION = 4
version = full_version
print(Language.version())
version = 함수 형태이죠.
생긴것은 property 같지만 사실 상 함수 이기때문에 (클래스명.변수값) 형태로 적어줘야지 버그없이 실행이 됩니다.
정의된 위치만 다를 뿐이지 실상 @staticmethod를 이용한 함수와 같다고 보시면됩니다.
마지막으로
해당 코드의 output은 어떻게 나올까요??
name = 'Guido'
class MyClass:
name = 'Raymond'
list_1 = [name for i in range(3)]
print(MyClass.list_1)
정답은...
['Guido', 'Guido', 'Guido'] 입니다.
"list_1은 property 인데 왜? 클래스내 변수를 사용하지 않지?" 라고 의문이 드시는 분도 있을 건데요.
list_1 은 comprehension 을 이용한 리스트입니다.
파이썬 내부적으로 comprehension 은 함수로서 작동 하기때문에 당연히 클래스내 값을 가져오지 않고 그 밖에서 값을 가져오는 것입니다.
'파이썬 > 파이썬 중급' 카테고리의 다른 글
[파이썬 중급] @property, @함수명.setter 사용법 (1) | 2021.07.23 |
---|---|
[파이썬 중급] 진법 변환(N진수 -> 10진수 | 10진수 -> N진수) (0) | 2021.06.18 |
[파이썬 중급] map, filter | zip + list comprehension (0) | 2021.05.18 |
[파이썬 중급] *args 사용법( 2편) (0) | 2021.05.04 |
[파이썬 중급] unpacking에 대해서 잘 알고 계시나요?(*, ** 사용법) (3) | 2021.04.22 |
댓글