[Python] 클래스, 상속, object클래스

3 분 소요

클래스

클래스 : 객체를 만들기 위한 일종의 설계도 객체 : 클래스를 기반으로 만들어진 실제 사물

클래스는 클래스내에 들어갈 변수와 메소드를 결정하는데 클래스에는 메소드 정의만 존재한다. 즉 변수의 선언은 존재하지 않는다. 객체 내에서 해당 변수를 대상으로 대입연산을 처음 진행하는 순간 객체 내에 변수가 생성된다.

>>> class Simple:
        def seti(self, i):
            self.i=i
        def geti(self):
            return self.i

>>> s1 = Simple()
>>> s1.seti(200)
>>> si.geti()
200
>>> s2 = Simple()
>>> s2.geti()
Trackback (most recent call last):
  File "<pyshell#18>" , line n, in <module>
    s2.geti()
  File "<pyshell#13>", line n, in geti
    return self.i
AttributeError: 'Simple' object has no attribute 'i'

위 상황에서 seti메소드를 먼저 실행해줘야만 한다. 그래야 객체 내에 변수 i가 만들어지기 때문이다.

>>> class Simple:
        def __init__(self):
            self.i = 200
        def seti(self, i):
            self.i=i
        def geti(self):
            return self.i

>>> s1 = Simple()
>>> si.geti()
200

위 코드와 같이 객체 생성 시 자동으로 호출되늰 init 메소드를 정의하면 위 상황과 같은 오류는 일어나지 않는다.

객체에 변수와 메소드 붙였다 떼었다 해보기

파이썬의 객체에는 변수와 메소드를 붙이기도 하고 떼기도 할 수 있다.

>>> class Simple:
        def geti(self):
            return self.i

>>> ss = Simple()
>>> ss.i = 27
>>> ss.geti()
27
>>> ss.hello = lambda : print('hi')
>>> ss.hello()
'hi'
>>> del ss.i
>>> del ss.hello

위 코드에서 객체 생성 이후 다음과 같은 형태로 변수 i를 객체에 추가했다. 이렇게 변수 i를 추가했기 때문에 geti메소드 호출이 가능하다. 또한 람다식을 기반으로 만든 함수를 객체에 추가했다.

이렇게 만든 변수와 메소드는 del 연산자를 통해 삭제할 수 있다.

클래스에 변수 추가하기

>>> class Simple:
        def __init__(self, i):
            self.i = i
        def geti(self):
            return self.i

>>> Simple.n = 7
>>> Simple.n
7

파이썬에서는 클래스에도 변수 추가가 가능하다.

클래스는 객체와 달리 설계도일 뿐인데 변수를 추가할 수 있는 이유는 “파이썬의 클래스는 클래스이자 객체이다”



파이썬에서는 클래스도 객체

>>> type
<class 'type'>
>>> type([1,2])
<class 'list'>
>>> class Simple:
      pass

>>> type(Simple)
<class 'type'>

type은 클래스의 이름이다.

사용자가 만든 클래스 Simple은 type클래스의 객체이다.

즉 클래스도 객체이다. 클래스는 type이라는 클래스의 객체이다.



상속

class Father:
    def run(self):
        print("so fast!!!")

class Mother:
    def dive(self):
        print("so deep!!")

class Son(Father, Mother):
    def jump(self):
        print("so high!!!")

def main():
    s = Son()
    s.run()
    s.dive()
    s.jump()

main()

상속을 하는 클래스를 부모 클래스, 슈퍼 클래스, 상위 클래스라고 한다. 상속을 받는 클래스를 자식 클래스, 서브 클래스, 하위 클래스라고 한다.

상속의 경우 위 코드와 같이 한번에 둘 이상의 클래스를 상속하는 것도 가능하다. 하지만 둘 이상의 클래스를 동시에 상속하면 구조가 복잡해지고 주의해야 할 사항들이 늘어나 일반적으로 둘 이상의 클래스를 동시에 상속하지 않는다.

메소드 오버라이딩과 super

class Father:
    def run(self):
        print("so fast, dad style")

class Son(Father):
    def run(self):
        print("so fast, son style")

    def run2(self):
        super().run()

def main():
    s = Son()
    s.run()
    s.run2()

main()

상속 관계에서 부모 클래스가 갖는 메소드와 동일한 이름의 메소드를 자식 클래스가 정의하는 경우를 메소드 오버라이딩이라고 한다. 이 경우 부모 클래스의 메소드는 보이지 않는 상태가 된다. 즉 호출이 불가능한 상태가 된다.

하지만 super()를 통해 부모 클래스의 메소드 호출이 가능하다.



init 메소드 오버라이딩

class Car:
    def __init__(self, id, f):
        self.id = id       
        self.fuel = f      

    def drive(self):
        self.fuel -= 10     

    def add_fuel(self, f):  
        self.fuel += f

    def show_info(self):
        print("id:", self.id)
        print("fuel:", self.fuel)

class Truck(Car):
    def __init__(self, id, f, c):
        super().__init__(id, f)
        self.cargo = c

    def add_cargo(self, c):
        self.cargo += c

    def show_info(self):       # 현재 차의 상태
        super().show_info()
        print("cargo:", self.cargo)


def main():
    t = Truck("42럭5959", 0, 0)
    t.add_fuel(100)
    t.add_cargo(50)
    t.drive()
    t.show_info()

main()

init 메소드의 경우 자식 클래스 init__메소드에서 부모 클래스의 __init 메소드를 호출이 가능하다.

즉 객체 생성 시 자동으로 호출되는 init 은 오버라이딩 해야 하는 메소드이면서 동시에 가려진 부모의 __init__을 반드시 호출해야 하는 메소드이다.



isinstance 함수

isinstance(object, classinfo) : 객체의 클래스 유형을 확인하는 함수

>>> class Simple:
      pass

>>> s = Simple()
>>> isinstance(s, Simple)   # s가 Simple 클래스의 객체인가?
True
>>> isinstance([1,2], list) # [1,2]가 list 클래스의 객체인가?
>>> class Fruit:
      pass

>>> class Apple(Fruit):
      pass
    
>>> class SuperApple(Apple):
      pass

>>> sa = SuperApple()
>>> isinstance(sa, SuperApple)
True
>>> isinstance(sa, Apple)
True
>>> isinstance(sa, Fruit)
True

상속 관계에 있는 경우에도 True이다.



object 클래스

파이썬의 모든 클래스는 object 클래스를 직접 혹은 간접 상속한다. 클래스를 정의할 때 object 클래스를 상속하도록 코드를 작성해야 한다는 뜻이다. 파이썬2 에서는 직접 object 클래스를 상속하도록 코드를 작성했지만 파이썬3에서는 이를 명시하지 않아도 된다.

>>> class Simple:
      pass

>>> isinstance(Simple(), object)  # Simple 객체가 object 클래스를 상속하는가?
True
>>> isinstance([1,2], object)     # 리스트는 object 클래스를 상속하는가?
True
>>> issubclass(type, object) # type 클래스는 object 클래스를 상속하는가?
True

심지어 type클래스도 object 클래스를 상속한다.

>>> dir(object)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

object 클래스의 내부에는 다음과 같은 메소드들이 있다.