Skip to content

24. 클래스 속성과 정적, 클래스 메서드


1. 키워드

  • 클래스 속성(Class Attribute)과 인스턴스 속성(Instance Attribute)
  • 비공개 클래스 속성(Private Class Attribute)
  • 정적 메서드(Static Method)
  • 데코레이터(Decorator)


2. 클래스 속성과 정적, 클래스 메서드 사용하기

  • 이번에는 클래스에 속해 있는 클래스 속성에 대해 알아보자.
  • 그리고 인스턴스를 만들지 않고 클래스로 호출하는 정적 메서드와 클래스 메서드도 사용해 보자.


3. 클래스 속성과 인스턴스 속성 알아보기

  • 속성에는 클래스 속성과 인스턴스 속성 두 가지 종류가 있다.
  • __init__ 메서드에서 만들었던 속성은 인스턴스 속성이다.


1) 클래스 속성 사용하기

  • 클래스 속성은 다음과 같이 클래스에 바로 속성을 만든다.


class 클래스이름:
    속성 = 


  • 다음과 같이 Person 클래스에 바로 bag 속성을 넣고, put_bag 메서드를 만들고, 인스턴스 두 개를 만든 뒤 각각 put_bag 메서드를 사용한다.


class Person:
    bag = []

    def put_bag(self, stuff):
        self.bag.append(stuff)

james = Person()
james.put_bag("책")

maria = Person()
maria.put_bag("열쇠")

print(james.bag) # ['책', '열쇠']
print(maria.bag) # ['책', '열쇠']


  • jamesmaria 인스턴스를 만들고 각자 put_bag 메서드로 물건을 넣었는데, james.bagmaria.bag을 출력해 보면 넣었던 물건이 합쳐져서 나온다.
  • 즉, 클래스 속성은 클래스에 속해 있으며 모든 인스턴스에서 공유한다.


001


  • put_bag 메서드에서 클래스 속성 bag에 접근할 때 self를 사용했다.
  • 하지만 self는 현재 인스턴스를 뜻하므로 클래스 속성을 지칭하기에는 조금 모호하다.


class Person:
    bag = []

    def put_bag(self, stuff):
        self.bag.append(stuff)


  • 그래서 클래스 속성에 접근할 때는 다음과 같이 클래스 이름으로 접근하면 좀 더 코드가 명확해진다.


클래스.속성
class Person:
    bag = []

    def put_bag(self, stuff):
        Person.bag.append(stuff)


  • Person.bag이라고 하니 클래스 Person에 속한 bag 속성이라는 것을 바로 알 수 있다.


  • 마찬가지로 클래스 바깥에서도 다음과 같이 클래스 이름으로 클래스 속성에 접근하면 된다.


print(Person.bag)


속성, 메서드 이름을 찾는 순서

  • 파이썬에서는 속성, 메서드 이름을 찾을 때 인스턴스, 클래스 순으로 찾는다.
  • 그래서 인스턴스 속성이 없으면 클래스 속성을 찾게 되므로 james.bag, maria.bag도 문제없이 동작한다.
  • 겉보기에는 인스턴스 속성을 사용하는 것 같지만 실제로는 클래스 속성이다.


  • 인스턴스와 클래스에서 __dict__ 속성을 출력해 보면 현재 인스턴스와 클래스의 속성을 딕셔너리로 확인할 수 있다.


인스턴스.__dict__
클래스.__dict__
>>> james.__dict__
{}
>>> Person.__dict__
mappingproxy({'__module__': '__main__', 'bag': ['책', '열쇠'], 'put_bag': <function Person.put_bag at ...>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None})


  • james.bag을 사용했을 때 클래스 속성을 찾는 과정은 다음과 같다.


002


2) 인스턴스 속성 사용하기

  • 가방을 여러 사람이 공유하지 않게 하려면 bag을 인스턴스 속성으로 만들면 된다.


class Person:
    def __init__(self):
        self.bag = []

    def put_bag(self, stuff):
        self.bag.append(stuff)

james = Person()
james.put_bag("책")

maria = Person()
maria.put_bag("열쇠")

print(james.bag) # ['책']
print(maria.bag) # ['열쇠']


  • james.bagmaria.bag을 출력해 보면 각자 넣은 물건만 출력된다.
  • 즉, 인스턴스 속성은 인스턴스별로 독립되어 있으며 서로 영향을 주지 않는다.
  • 이제 클래스 속성과 인스턴스 속성의 차이점을 정리해 보자.


1] 클래스 속성

  • 모든 인스턴스가 공유
  • 인스턴스 전체가 사용해야 하는 값을 저장할 때 사용

2] 인스턴스 속성

  • 인스턴스별로 독립되어 있음
  • 각 인스턴스가 값을 따로 저장해야 할 때 사용


3) 비공개 클래스 속성 사용하기

  • 클래스 속성도 비공개 속성을 만들 수 있다.
  • 클래스 속성을 만들 때 __속성과 같이 __(밑줄 두 개)로 시작하면 비공개 속성이 된다.
  • 따라서 클래스 안에서만 접근할 수 있고, 클래스 바깥에서는 접근할 수 없다.


class 클래스이름:
    __속성 = 


  • 즉, 클래스에서 공개하고 싶지 않은 속성이 있다면 비공개 클래스를 사용해야 한다.


class Knight:
    __item_limit = 10

    def print_item_limit(self):
        print(Knight.__item_limit)

x = Knight()

x.print_item_limit() # 10

print(Knight.__item_limit) # AttributeError: type object 'Knight' has no attribute '__item_limit'


  • 실행을 해보면 클래스 Knight의 비공개 클래스 속성 __item_limit은 클래스 안의 print_item_limit 메서드에서만 접근할 수 있고, 클래스 바깥에서 접근하면 에러가 발생한다.
  • 이처럼 비공개 클래스 속성은 클래스 바깥으로 드러내고 싶지 않은 값에 사용한다.


클래스와 메서드의 독스트링 사용하기

  • 함수와 마찬가지로 클래스와 메서드도 독스트링을 사용할 수 있다.


  • 다음과 같이 클래스와 메서드를 만들 때 :(콜론) 바로 다음 줄에 """ """(큰따옴표 세 개) 또는 ''' '''(작은따옴표 세 개)로 문자열을 입력하면 된다.


class Person:
    """사람 클래스입니다."""

    def greeting(self):
        """인사 메서드입니다."""

        print("Hello")

print(Person.__doc__) # 사람 클래스입니다.
print(Person.greeting.__doc__) # 인사 메서드입니다.

maria = Person()

print(maria.greeting.__doc__) # 인사 메서드입니다.


  • 그리고 클래스의 독스트링은 클래스.__doc__ 형식으로 사용하고, 메서드의 독스트링은 클래스.메서드.__doc__ 또는 인스턴스.메서드.__doc__ 형식으로 사용한다.


4. 정적 메서드 사용하기

  • 이번에는 인스턴스를 통하지 않고 클래스에서 바로 호출할 수 있는 정적 메서드와 클래스 메서드에 대해 알아보자.


  • 먼저 정적 메서드는 다음과 같이 메서드 위에 @staticmethod를 붙이며, 이때 정적 메서드는 매개변수 self를 지정하지 않는다.


class 클래스이름:
    @staticmethod
    def 메서드(매개변수1, 매개변수2):
        코드


  • @staticmethod처럼 앞에 @이 붙은 것을 데코레이터라고 하며 메서드(함수)에 추가 기능을 구현할 때 사용한다.


  • 간단하게 덧셈과 곱셈을 하는 클래스를 만들어보자.


class Calc:
    @staticmethod
    def add(a, b):
        print(a + b)

    @staticmethod
    def mul(a, b):
        print(a * b)

Calc.add(10, 20) # 30
Calc.mul(10, 20) # 200


  • Calc 클래스에서 @staticmethod를 붙여서 add 메서드와 mul 메서드를 만들었다.


  • 정적 메서드를 호출할 때는 다음과 같이 클래스에서 바로 메서드를 호출하면 된다.


클래스.메서드()
Calc.add(10, 20)
Clac.mul(10, 20)


  • 정적 메서드는 self를 받지 않으므로 인스턴스 속성에는 접근할 수 없다.
  • 그래서 보통 정적 메서드는 인스턴스 속성, 인스턴스 메서드가 필요없을 때 사용한다.
  • 정적 메서드는 메서드의 실행이 외부 상태에 영향을 끼치지 않는 순수 함수(Pure Function)를 만들 때 사용한다.
  • 순수 함수는 부수 효과(Side Effect)가 없고 입력 값이 같으면 언제나 같은 출력 값을 반환한다.
  • 즉, 정적 메서드는 인스턴스의 상태를 변화시키지 않는 메서드를 만들 때 사용한다.


파이썬 자료형의 인스턴스 메서드와 정적 메서드

  • 파이썬의 자료형도 인스턴스 메서드와 정적, 클래스 메서드로 나뉘어져 있다.


  • 예를 들어 다음과 같이 세트에 요소를 더할 때는 인스턴스 메서드를 사용하고, 합집합을 구할 때는 정적 메서드를 사용하도록 만들어져 있다.


>>> a = {1, 2, 3, 4}

>>> a.update({5})

>>> a
{1, 2, 3, 4, 5}

>>> set.union({1, 2, 3, 4}, {5})
{1, 2, 3, 4, 5}


  • 이처럼 인스턴스의 내용을 변경해야 할 때는 update와 같이 인스턴스 메서드로 작성하면 되고, 인스턴스 내용과는 상관없이 결과만 구하면 될 때는 set.union과 같이 정적 메서드로 작성하면 된다.


5. 클래스 메서드 사용하기

  • 이번에는 정적 메서드와 비슷하지만 약간의 차이점이 있는 클래스 메서드를 사용해 보자.


  • 클래스 메서드는 다음과 같이 메서드 위에 @classmethod를 붙이며, 이때 클래스 메서드는 첫 번째 매개변수에 cls를 지정해야 한다.


class 클래스이름:
    @classmethod
    def 메서드(cls, 매개변수1, 매개변수2):
        코드


  • 사람 클래스 Person을 만들고 인스턴스가 몇 개 만들어졌는지 출력하는 메서드를 만들어보자.


class Person:
    count = 0

    def __init__(self):
        Person.count += 1

    @classmethod
    def print_count(cls):
        print(f"{cls.count}명 생성되었습니다.")

james = Person()
maria = Person()

Person.print_count() # 2명 생성되었습니다.


  • 먼저 인스턴스가 만들어질 때마다 숫자를 세어야 하므로 __init__ 메서드에서 클래스 속성 count1을 더해 준다.
  • 물론 클래스 속성에 접근한다는 것을 명확하게 하기 위해 Person.count와 같이 만들어준다.


class Person:
    count = 0

    def __init__(self):
        Person.count += 1


  • 이제 @classmethod를 붙여서 클래스 메서드를 만든다.
  • 클래스 메서드는 첫 번째 매개변수가 cls인데 여기에는 현재 클래스가 들어온다.
  • 따라서 cls.count처럼 cls로 클래스 속성 count에 접근할 수 있다.


    @classmethod
    def print_count(cls):
        print(f"{cls.count}명 생성되었습니다.")


  • Person으로 인스턴스를 두 개 만들었으므로 print_count를 호출해 보면 "2명 생성되었습니다."가 출력된다.
  • 물론 print_count는 클래스 메서드이므로 Person.print_count()처럼 클래스로 호출해 준다.


james = Person()
maria = Person()

Person.print_count() # 2명 생성되었습니다.


  • 클래스 메서드는 정적 메서드처럼 인스턴스 없이 호출할 수 있다는 점은 같다.
  • 하지만 클래스 메서드는 메서드 안에서 클래스 속성, 클래스 메서드에 접근해야 할 때 사용한다.
  • 특히 cls를 사용하면 메서드 안에서 현재 클래스의 인스턴스를 만들 수도 있다.
  • 즉, cls는 클래스이므로 cls()Person()과 같다.


    @classmethod
    def create(cls):
        p = cls()

        return p

References