강의노트 07. [객체지향] 클래스 / 인스턴스 변수, 클래스 / 인스턴스 / 스태틱 메소드

|

패스트캠퍼스 컴퓨터공학 입문 수업을 듣고 중요한 내용을 정리했습니다. 개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.

클래스 변수 / 인스턴스 변수

예시

class Point:
    a = 10 # 클래스 변수
    def __init__(self, _x, _y):
        self.x = _x # 인스턴스 변수
        self.y = _y

    @classmethod
    def class_method(cls):
        print("클래스 메소드 호출!", cls.a)

    def instance_func(self): # 인스턴스 메소드
        print("인스턴스 메소드 호출!")

    def __str__(self):
        return "({}, {})".format(self.x, self.y)


  • 인스턴스 메소드는 인스턴스가 있어야 실행이 가능하다.
Point.instance_func() # TypeError 발생
  • 클래스 메소드는 클래스 자체로 실행 가능하다.
Point.class_method() # 클래스 메소드 호출! 10
  • __dict__ 를 통해서 객체의 심볼테이블(변수를 저장해 놓는 공간)를 확인 가능하다.
p1 = Point(1, 2)
p1.__dict__  # 인스턴스의 심볼 테이블 확인
# {'x': 1, 'y': 2}

Point.__dict__  # 클래스의 심볼 테이블 확인
# 'a': 10,
# 'class_method': <classmethod at 0x10d5a2ba8>,
# 'instance_func': <function __main__.Point.instance_func>})
  • 인스턴스가 클래스 변수에 접근하여 수정할 수 없다.
Point.a # 10
p1.a # 10
p1.a = 5 # 인스턴스 멤버 생성
Point.a # 10 클래스 멤버 a는 수정되지 않는다.
p1.__dict__  # {'a': 5, 'x': 1, 'y': 2} 새로 정의한 인스턴스 멤버 a 추가

클래스 변수

  • 모든 인스턴스 객체가 공유한다.
  • 인스턴스가 클래스 변수에 접근하여 수정할 수 없다.
class Person:
    #클래스 변수, 클래스 멤버 : 모든 객체가 공유한다
    planet = 'Earth'

    def __init__(self, name, age, money):
    	#객체변수, 인스턴스 멤버 초기화
        self.name = name
        self.age = age
        self.money = money

클래스 메소드 / 인스턴스 메소드 / (참고) static 메소드

이 세 가지 메소드는 모두 클래스 안에서 정의 됩니다. 인스턴스 메소드는 인스턴스를 통해서 호출이 되고, 첫 번째 인자로 인스턴스 자신을 자동으로 전달합니다. 관습적으로 이 인수를 ‘self’라고 칭합니다. 클래스 메소드는 클래스를 통해서 호출이 되고 “@classmethod”라는 데코레이터로 정의합니다. 첫 번째 인자로는 클래스 자신이 자동으로 전달되고 이 인수를 관습적으로 ‘cls’라고 칭합니다. 스태틱 메소드는 앞서 설명한 두 메소드와는 틀리게 인스턴스나 클래스를 첫 번째 인자로 받지 않습니다. 스태틱 메소드는 클래스 안에서 정의되어 클래스 네임스페이스 안에는 있을뿐 일반 함수와 전혀 다를게 없습니다. 하지만 클래스와 연관성이 있는 함수를 클래스 안에 정의하여 클래스나 인스턴스를 통해서 호출하여 조금 편하게 쓸 수가 있는 것 입니다. 출처

class method

  • 주로 생성자 오버로딩으로 쓰인다.
  • 메소드를 클래스의 멤버로 선언한다.
  • class 자체를 매개변수 (cls)로 받는다. (인스턴스 메소드는 self인 인스턴스를 인자로 받음)
  • @classmethod 데코레이터를 사용한다.
# 클래스 함수
# class method : 첫번째 매개변수로 class를 자동으로 받는다.
   # 주로 생성자 오버로딩으로 쓰인다.(파이썬에서는 원칙적으로 생성자 오버로딩 안됨)
   @classmethod
   def FromString(cls, string):
       name, age_int, money_int = string.split('-')
       age = int(age_int)
       money = int(money_int)
       return cls(name, age, money)

   #클래스 변수의 값을 변경할 때 사용한다.
   @classmethod
   def ChangePlanet(cls, pln):
       cls.planet = pln
  • 클래스 변수를 변경할 때, 직접 변경하는 방법도 있지만 데이터 검사나 다른 부가 기능등의 추가가 필요할 때 클래스 메소드를 이용하면 아주 편리함
  • 예시 : 직원들의 연봉 인상률을 일괄 변경할 때 인상률은 1 이상으로만 변경 가능하도록 한다. (데이터 무결성 검사를 실시한 후, 데이터가 1보다 큰 경우에만 클래스변수를 변경)
class Employee:
	raise_amount = 1.1 # 연봉 인상률 클래스 변수

	#...생략

	@classmethod
	def change_raise_amount(cls, amount):
		while amount < 1: # 데이터 무결성 검사
			print('[경고] 인상률은 1보다 작을 수 없습니다.')
			amount = input('[입력] 인상률을 다시 입력해 주세요 \ㅜn=> ')
			amount = float(amount)
		cls.raise_amount = amount
		print('인상률 {}가 적용되었습니다.'.format(amount))

# 실행예시
Employee.change_raise_amount(0.9)
# [경고] 인상율은 "1"보다 작을 수 없습니다.
# [입력] 인상율을 다시 입력하여 주십시오.
# => 1.2
# 인상율 "1.2"가 적용 되었습니다.

instance method

  • 인스턴스 메소드는 인스턴스가 있어야 실행이 가능하다.
# 객체 함수
def giveMoney(self, other,how_much):
    if how_much <=self.money:
        other.money +=how_much
        self.money -= how_much
    else:
        print("you don't have {0}".format(how_much))

def showMyInfo(self):
    print("{0} has {1}".format(self.name, self.money))

(참고) static method

  • @staticmethod 데코레이터를 사용하여 선언한다.
  • @classmethod 와는 다르게 cls 매개변수를 받지 않는다. 이로 인해서 상속받는 경우 동작이 달라진다.

예시 1

class momClass(object):
    name = 'mom'

    @staticmethod
    def get_name_static():
        print(momClass.name)

    @classmethod
    def get_name_class(cls):
        print(cls.name)

# momClass를 상속받는 sonClass 정의
class sonClass(momClass):
    name = 'son'

sonClass.get_name_static() # mom : momClass의 name 속성을 반환  
sonClass.get_name_class() # son : sonClass의 name 속성을 반환

예시 2

class foo():
    @classmethod
    def set_name(cls, name):
        cls.name = name

class bar(foo):
    pass

foo.set_name('foo')
foo.name # foo

bar.set_name('bar')
bar.name # bar

예시 3

class foo():
    @staticmethod
    def set_name(name):
        foo.name = name

class bar(foo):
    pass

foo.set_name('foo')
foo.name # foo
bar.name # foo

bar.set_name('bar')
foo.name # bar
bar.name # bar