ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 파이썬 클래스, 인스턴스, self 이해하기
    Python/이것저것 파이썬 2024. 8. 8. 11:12
    반응형
    먼저 아래 링크 읽어보시면 좋겠네요. 

    https://wikidocs.net/28

     

    05-1 클래스

    초보 개발자들에게 클래스(class)는 넘기 힘든 장벽과도 같은 존재이다. 독자들 중에도 클래스라는 단어를 처음 접하는 이들이 있을 것이다. 그러면 도대체 클래스가 무엇인지, 클…

    wikidocs.net

    과자 틀 = 클래스
    과자 틀로 찍어 낸 과자 = 인스턴스

    좋은 비유인 것 같습니다.  

     

    혹시 위 링크를 읽어도 이해가 안 되신다면 아래 글을 읽어보십시오. 

    객체 지향을 처음 접한 분의 질문에
    제가 답해 드린 적이 있는데,
    그 내용을 정리한 것입니다.

    객체지향의 '문법'적인 부분을
    쉬운 표현과 비유로 설명드리려고 노력했습니다만......

     

    1. 빵과 빵틀

    점프 투 파이썬에서는 쿠키와 쿠키틀이지만,
    표절할 수는 없으므로 빵과 빵틀로 코딩하겠습니다. ^^ 

    class Bread:
    
        def bake(self):  # static 메서드 이긴 하다. 일단 pass 
            print('빵을 굽습니다.')
    
    
    bread1 = Bread()  # (1)
    bread2 = Bread()
    bread3 = Bread()
    
    bread1.bake()  # (2)

     

    Bread라는 클래스를 작성하였습니다.

    파이썬에서는 클래스 작성 시 CamelCase라는 표기법을 씁니다.
    단어를 띄우지 않고 단어의 시작을 대문자로 쓰는 방식입니다. 

     

    그리고 Bread에는 bake라는 메서드를 작성했습니다. 

    클래스 안의 함수를 메서드(method)라고 합니다. 

     

    1.1. 인스턴스를 만들어 보자.

    bread1 = Bread()  # (1)

    (1) 여기서 봐야 할 것은 Bread라는 클래스 명 뒤에 '()' 괄호를 붙였다는 겁니다. 
    이 괄호를 붙임으로써 이 클래스는 실행이 됩니다. 
    이 실행은 어떤 동작을 한 걸까요?

    인스턴스를 만드는 동작을 합니다.
    빵틀(클래스)로 빵(인스턴스)을 찍어 만든 것입니다.
    Bread 클래스로 bread1이라는 인스턴스를 찍어냈습니다.  

    오브젝트와 클래스와 인스턴스의 차이를 알아봅시다. 
    '클래스'는 '오브젝트의 설계도'이고,
    '인스턴스'는 '오브젝트를 실체화한(찍어낸) 것'을 의미하죠.
    한국어로는 인스턴스와 오브젝트 둘 다 객체라고 번역합니다만..
    이런 차이가 있습니다.  그냥 영어로 쓰는 게 편한 것 같습니다.  

     

    1.2. 메서드를 실행해 보자. 

    bread1.bake()  # (2)

    이렇게 만들어진 인스턴스를 이용해
    "인스턴스명.메서드명()" 형식으로
    클래스에서 정의한 메서드를 실행할 수 있습니다. 

     

    2. 각각의 인스턴스에 변수를 달아볼까?

    오브젝트(객체)라는 것은 데이터와 함수(메서드)의 조합입니다.

    데이터와 함수를 함께 모아서 정리할 방법이 있다는 것은
    프로젝트가 커질수록 큰 장점이 됩니다.

    처음 객체지향 프로그래밍을 접할 때는
    큰 프로젝트를 접한 경험이 없으니
    객체지향의 장점을 이해하기 힘듭니다.

    GUI가 있는 프로그램에 기능을 추가하면,
    금방 코드가 길어지니 
    tk나 QT로 코딩을 해보시는 것도 좋을 겁니다. 
    업데이트 알림, 광고기능.... 

     

    함수는 사용해 보았으니
    이제 데이터의 가장 단순한 형태인 변수를 인스턴스에서 사용해 봅시다.    

    class Bread:
    
        def __init__(self, name):
            self.name = name
    
        def bake(self):
            print('빵을 굽습니다.')
    
    
    bread1 = Bread()  # (1)
    bread2 = Bread()
    print(bread1.name, bread2.name)
    
    bread1.bake()

    일단 '__init__'라는 함수(메서드)가 추가되었네요. 

    여기에는 뭔지 잘 모르겠지만 self라는 단어도 보이고, 
    name이라는 변수도 보입니다. 

    파이썬에서 
    인스턴스의 변수를 이용하기 위해서는 초기화 메서드가 필요합니다. 
    이 초기화 메서드를 작성한 것인데요. 
    조금 더 자세히 알아보겠습니다. 

    파이썬에서 '__double_leading_and_trailing_underscores__'으로 네이밍 된 변수나 메서드는
    특정한 문법적 기능을 제공하거나 특정한 일을 수행한다.
    ex) '__init__'는 초기화라는 특별한 일을 한다. 

    https://mingrammer.com/underscore-in-python/

     

    3. 초기화~!

    실행해 보면 에러가 납니다. 
    어떤 에러이고, 왜 에러가 난 걸까요?

    bread1 = Bread()  # (1)
                 ^^^^^^^
    TypeError: Bread.__init__() missing 1 required positional argument: 'name'

    Bread() ← 이 괄호 안에...
    name이라는
    1개의 위치지정 인수(argument)가 없다... 
    라는 에러 메시지가 뜹니다. 

    함수 '호출' 시 괄호 안의 '값'(함수 내부로 전달되는 값)을 인수(argument)라고 합니다. 
    여러 개의 아귀먼트가 있을 때 파이썬에는 위치 또는 이름으로 각각을 구분할 수 있습니다.

    https://wikidocs.net/24#_10

     

    참고로 매개변수(parameter)는 함수 '정의' 시 괄호 안의 '변수'를 의미합니다. 

     

    '__init__'라는 메서드는 initialize(초기화)라는 단어에서 나온 말이겠죠? 
    우리가 Bread라는 클래스로 bread1이라는 인스턴스를 만들 때
    파이썬에 의해 자동으로 실행되는 메서드가 바로 '__init__'입니다. 

    만약 당신이 __init__라는 함수를 작성했다면,
    객체 생성 시 자동으로 실행할 테니
    객체를 초기화할 때 사용하라는 의미죠. 

     

    (자동으로) '__init__'를 실행했는데 아귀먼트가 하나 비어있으니 채워 넣으라는 에러입니다. 

     

    4. 에러를 잡아보자

    에러를 잡기 위해서는 괄호 안을 채워줘야겠죠?

    class Bread:  # (1)
    
        def __init__(self, name):  # (2)
            self.name = name
    
        def bake(self):
            print('빵을 굽습니다.')
    
    
    bread1 = Bread('단팥빵')  # (3)
    bread2 = Bread('소보로빵')
    print(bread1.name, bread2.name)
    
    bread1.bake()

    이제 잘 작동합니다. 

    위 코드에서

    (1) "class Bread:"의  'Bread'라는 클래스명과
    (2) "def __init__(self, name):"에서는 self를 제외한 'name'이 합쳐져서
    (3) "bread1 = Bread('단팥빵')"에서 Bread('단팥빵')의 형태가 된 것이 보이시죠? 

    이 형태를 잘 기억해 두십시오. 
    (뒤에 그림으로 나옴~!)

     

    5. 그럼 self는 뭐야?

    class Bread:
    
        def __init__(self, name):
            self.name = name
    
        def bake(self):
            print('빵을 굽습니다.')
    
    
    bread1 = Bread('단팥빵')
    bread2 = Bread('소보로빵')
    print(bread1.name, bread2.name)
    
    bread1.name = '달고단팥빵'
    print(bread1.name, bread2.name)
    
    bread1.bake()

    'self.name'이 인스턴스 생성(초기화) 후에
    bread1에서는 bread1.name으로, bread2에서는 bread2.name으로 변했죠?

    그럼 self는 뭐다?

    bread1이나 bread2로 변할 수 있는 무언가다.
    우린 혹시 bread1, bread2를 인스턴스라고 부르기로 하지 않았던가요?

    self는 인스턴스 이름이 들어갈 자리를 비워 놓은 거다. 
    인스턴스 그 자체(?)라는 의미로 self라고 했구나. 

    클래스를 정의할 때는
    클래스가 어떤 인스턴스가 될지 모르는 상태니까
    특정한 이름을 정할 수 없었고, 'self'라는 표현을 쓴 것이군. 

     

    그림으로 정리하면 더 쉽습니다.
    형태를 꼭 기억해 두세요. 

    class Bread:
    
        def __init__(self, name):  # (1)
            self.name = name  # (2)
    
    bread1 = Bread('단팥빵')  # (3)

    (1)에서 name는 (3)의 괄호 사이에서 전달받은 것이고
    (1) name은 바로 (2) name으로 내려가고
    bread1은 (1)과 (2)의 self라는 사실~! 

    그러면 이제 __init__ 메서드가 이해되실 겁니다. 

    self.name = name  # (2)

    인스턴스의 name 변수(bread1.name)에 
    인스턴스 생성 시의 name 매개변수의 값('단팥빵')을 넣어라.

     

    점프 투 파이썬에 나오는 다음 내용 또한 이해되실 겁니다. 

     

    6. 이걸 왜 만들었지? 어떻게 써먹음?

    class Bread:
    
        def __init__(self, name):
            self.name = name  # (1)
    
        def bake(self):  # (2)
            print(f'{self.name}을 굽습니다.')  # (3)
    
    
    bread1 = Bread('단팥빵')
    bread2 = Bread('소보로빵')
    bread1.bake()
    
    bread1.name = '달고단팥빵'
    bread1.bake()
    bread2.bake()

    (1) __init__ 메서드를 이용해서 만든 'self.변수'를 우리는 인스턴스 변수라고 합니다. 
    여기에는 인스턴스 각각의 고유 값들을 넣을 수 있습니다.

    (3) 이것을 위 코드처럼 클래스 내부의 메서드에서 사용할 수도 있죠.
    이렇게 되면 각각의 인스턴스에 따라 다른 빵 이름을 하나의 메서드로 출력가능합니다.  

    (2) 메서드의 매개변수(parameter)에도 'self'라는 표현이 필요합니다.  
    메서드 내에서 '인스턴스 변수(3)'나 이 클래스 내의 다른 '메서드'를 사용해야 하기 때문입니다. 
    이 메서드가 나중에 어떤 인스턴스에서 사용될지 모르니까. ㅇㅇ

     

    7. 좀 더  나아가 보자. 

    7.1. 정적 메서드

    self가 필요 없는 메서드도 있습니다.
    만약 인스턴스 변수나 다른 메서드를 쓰지 않으면 self를 쓸 필요가 없죠. 
    이 글의 첫 코드에서 bake 메서드가 그렇습니다. 

    self를 선언만 했지 사용하지 않았습니다.
    그럼 좀 더 깔끔하게 self를 쓰지 않을 수는 없을까요?
    (그냥 지우면 에러 뜸 ㅡㅡ;)

    파이참에서는 노란줄이 bake 함수 아래에 ...

     

    다음과 같이 '@staticmethod' 데코레이터를 쓰면 됩니다.

    Static method는 정적 메서드라고 번역합니다.  
    class Bread:
    
        @staticmethod
        def bake():
            print('빵을 굽습니다.')
    
    
    bread1 = Bread()
    bread1.bake()

    좀 더 명확하고 가독성도 좋아집니다. 

    @staticmethod 데코레이터의 의미..

    이 bake 메서드는 self를 안 쓰는 메서드야.
    즉 꼭 클래스 내부에 작성할 필요가 없는 메서드지.
    하지만 모아놓는 게 유지보수가 편할 것 같아서 모아 놨어.
    bake 메서드가 Bread 클래스 밖에 있는 것도 이상하지?
    데코레이터 달아놨으니까, self 뺀다. 에러 띄우지 마..
    (이런 식으로 이해할 수도 있겠죠 ^^)

     

    7.2. 클래스 변수

    그러면 클래스 전체가 공유하는 값은 어떻게?
    그건 클래스 변수라고 합니다. 
    다음 글을 참고하십시오. 
    https://wikidocs.net/1744

    static 변수 = 정적 변수 = 클래스 변수 = 공용 변수
    파이썬에서 클래스 문법은
    알고 보면 직관적(??)이고 명시적인데,
    처음 보면 복잡해 보이기도 합니다. 

    왜 명시적이라고 할까요? 
    '인스턴스가 생성될 때 __init__ 메서드가 작동하니까 그때 인스턴스 변수가 생기는구나.'
    '클래스 변수는 클래스 선언할 때 같이 작성했으니, 클래스가 로딩되면 클래스 변수가 생성되겠네...'
    이걸 따로 외우지는 않죠? 

    자바에서는 __init__ 메서드를 쓰지 않고
    클래스 내에 변수를 선언하면 인스턴스 변수가 되고, 
    앞에 static을 붙여주면 클래스 변수가 됩니다.
     
    문법적으로는 자바가 더 깔끔한데,
    별도의 설명 없이 인스턴스 변수와 클래스 변수의 차이를 이해하기가 힘듭니다.  

    자바에서 인스턴스 변수와 클래스 변수의 차이

     

    7.3. 클래스 메서드

    정적 메서드를 알면 클래스 메서드도 어렵지 않습니다.
    https://www.daleseo.com/python-class-methods-vs-static-methods/

     

    반응형
Designed by Tistory.