Python 기본 - Class

2 minute read

파이썬 웹 프레임워크인 Django, Flask를 공부하면서 정작 기반이 되는 파이썬 언어 자체의 기본에 대해 취약해서 혹시나 면접같은 장소에서 깊은 질문을 받으면 어버버거릴 가능성이 너무 높다는 걸 느꼈다. Django 프로젝트의 CBV와 테스트 코드 작성을 진행하면서 자주 등장했던 @classmethod 데코레이터가 무슨 역할을 하는지도 궁금해졌다. 이 데코레이터를 알기 위해선 파이썬 클래스의 개념부터 잘 알아야 한다.

시작해보자.

Class

파이썬에서 클래스 생성은 여타 언어와 다르게 new 키워드를 사용하지 않는다.

기본적으로 SnakeCase를 사용하는 변수와 함수와 달리 클래스의 네이밍컨벤션은 CamelCase를 사용한다.

# airtravel.py

class Flight:
    def __init__(self):
        print('init')
        super().__init__()

    def __new__(cls):
        print('new')

        return super().__new__(cls)

    def number(self):
        return 'SN060'

위 코드가 기본적인 클래스와 인스턴스를 생성하는 코드인데 __init__ 메소드는 self, __new__ 메소드는 cls를 argument로 사용하고 있다. 이 부분은 유닛 테스트를 진행하면서 굉장히 많은 의문이 들었다.

결론적으로, cls를 argument로 받는 메소드는 클래스 메소드이고, self를 argument로 받는 메소드는 인스턴스 메소드이다. 클래스 메소드는 @classmethod라는 데코레이터를 사용하여 선언한다.

클래스 메소드는 인스턴스를 따로 선언할 필요 없이 호출할 수 있다.

class ClassMethod:
  @classmethod
  def print_name(cls):
    print('My name is %s' % (cls.__class__.__name__))
>>> ClassMethod.print_name()
My name is type
>>> from airtravel import Flight
>>> f = Flight()
new
init

적지 않은 사람들이 __init__이 생성자라고 말하는데 생성자가 아니다. __new__ 메소드가 생성자이다. 위 코드처럼 f = Flight()생성자로 객체를 생성하라고 호출받으면 __new__ 메소드를 호출하여 객체를 생성 및 할당하고, __new__메소드가 __init__ 메소드를 호출하여 객체에서 사용할 값들을 초기화한다.

파이썬에서 일반적으로 클래스를 생성할 때 __init__ 메소드만 오버라이딩하여 생성이 아닌 객체 초기화에만 이용한다.

클래스 변수와 인스턴스 변수

self 객체가 인스턴스 자체를 가리키기 때문에 __init__ 메소드에 전달하는 self.AAA 파라미터는 인스턴스 변수이다. 반면에 클래스 내에서 self없이 선언하는 변수를 클래스 변수라고 한다. 클래스 내에서 공유되기 때문에 이 클래스로 생성한 모든 인스턴스에서 사용할 수 있다.

# self없이 클래스 내에서 바로 선언한다.

class Sample:
  AAA = aaa
  
  def method():
    pass
  
  ...
class Flight:
    class_attr = []

    def add_class_attr(self, number):
        Flight.class_attr.append(number)


first = Flight()
second = Flight()

first.add_class_attr(11)
print(Flight.class_attr)	# 11
print(first.class_attr)		# 11

위 코드를 보면 분명히 g 인스턴스를 통해 class_attr을 할당해주지 않았는데도 동일한 값을 출력하고 있다. 여러 인스턴스가 클래스 변수을 공유하고 있음을 알 수 있다.

클래스 변수과 인스턴스 변수를 같은 이름으로 선언해보자.

class Flight:
    class_attr = []

    def __init__(self):
        self.class_attr = []

    def add_class_attr(self, number):
        Flight.class_attr.append(number)

    def add_instance_attr(self, number):
        self.class_attr.append(number)


first = Flight()
second = Flight()

first.add_class_attr(5)
print(first.class_attr)		# [5]
print(Flight.class_attr)	# []
print(second.class_attr)	# []

클래스 변수과 인스턴스 변수를 동일하게 선언 후 호출해보면 클래스 변수보다 인스턴스 변수를 먼저 접근하는 것을 확인할 수 있다.

상속

파이썬의 클래스 상속은 별도의 예약어를 사용하지 않고 선언하는 클래스명 뒤에 괄호로 감싸서 상속받을 부모 클래스명을 써주면 된다.

class Parent:
  pass

class Child(Parent):
	pass

C나 Java와 다르게 파이썬은 다중 상속을 지원한다. (C++도 다중 상속을 지원한다고 한다.)

갯수에 상관없이 상속받은 각 클래스의 변수와 메소드를 모두 사용할 수 있다.

class Football:
  rule = []


class Baseball:
  rule = []


class Basketball:
  rule = []


class Sports(Football, Baseball, Basketball):
  print(Football.rule)
  print(Baseball.rule)
  print(Basketball.rule)

mro() 메소드로 클래스들의 상속관계를 확인할 수 있다. 오른쪽으로 갈수록 상위의 클래스이다. 모든 클래스는 Object 클래스를 상속받기 때문에 맨 오른쪽에는 항상 <class 'object'>가 출력된다.

print(Sports.mro())

# [<class '__main__.Sports'>, <class '__main__.Football'>, <class '__main__.Baseball'>, <class '__main__.Basketball'>, <class 'object'>]

정적 메소드

정적 메소드는 클래스에서 직접 접근 및 참조가 가능한 메소드이다. 인스턴스를 통하지 않고 직접 접근할 수 있는 클래스 변수처럼 클래스명 뒤에 바로 메소드를 호출할 수 있다.

파이썬에서 정의한 정적 메소드에는 staticmethodclassmethod가 있는데 staticmethod는 self처럼 추가되는 argument가 없는 반면, classmethod는 첫 번째 argument로 클래스(cls)를 입력한다.

Categories:

Updated: