본문 바로가기
Python

[Python 기초] 파이썬 클래스 - 오버라이딩 (overriding)

by dev수니 2021. 4. 5.
반응형

 

 

 

예전의 포스팅에서 클래스에대해 간단하게 설명하고 넘어갔었다. 클래스(객체)라는 것에 대해 완벽히 숙지하기 위해서 공부해야 할 것들이 많은데 그 중 하나가 오버라이딩이다.

 

또한 오버라이딩과 같이 붙어다니는 함수인 super() 함수도 있는데 그럼 오늘은 오버라이딩super() 함수에 대해서 공부해보자.

 

 


 

 

 1  오버라이딩

 

부모로부터 받은 메서드 내용을 자식클래스에 맞게 내용 변경, 재정의 하는것이다.

 

오버라이딩 조건

부모클래스의 메서드명과 자식클래스의 메서드명이 동일해야 한다.

 

 

이를 예제를 통해 알아보자.

 

 

# 동물 클래스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Animal:
     def __init__(self,name):
          self.name = name
 
     def cry(self):
          pass
 
class Dog(Animal):
     def __init__(self,name,age):
          self.name = name
          self.age = age
 
     def cry(self):    # 메서드 오버라이딩 : 메서드 내용을 재정의 해줬다.
          print("멍멍")
 
class Cat(Animal):
     def __init__(self,name,age):
          self.name = name
          self.age = age
          
     def cry(self,str1):    # 메서드 오버라이딩 : 메서드 내용을 재정의 해줬다.
          print(self.name,"가 운다. ",str1,sep="")
 
dog1 = Dog("멍머이",3)
dog1.cry()
 
cat1 = Cat("나비",2)
cat1.cry("야오오옹~")
cs
멍멍
나비가 운다.
야오오옹~
>>>

먼저 부모클래스의 생성자를 통해 name을 받아오고 cry 메서드를 생성한다. 그리고 부모클래스를 상속받는 Dog 클래스내에서 생성자로 name과 age 를 받아오고 인스턴스 변수 self.name과 self.age에 각각 저장한다.

 

그리고 클래스 변수로 부터 물려받은 cry()메서드를 줄14의 내용으로 재정의(메서드 오버라이딩) 해주었다.

 

또 Cat 클래스도 Animal 클래스를 상속받아 생성하고 생성자를 통해 Dog 클래스와 똑같이 변수를 전달받고 cry() 메서드를 오버라이딩 해주었다.

 

그리고 줄 24에 Dog인스턴스  생성 후 cry()메서드를 호출해 주었을 때 Dog클래스에서 재정의해준 내용이 출력되었고,

Cat인스턴스 생성 후 cry() 메서드를 호출 해 주었을 때 Cat 클래스에서 재정의해준 내용이 출력되었다.

 

 

 

 

 

이 처럼 부모클래스로 부터 물려받은 내용을 자식클래스 내에서 재정의 해주어 사용할 수 있는 오버라이딩을 사용하여 메소드를 더 생성하지 않고 각각의 클래스에 맞게 수정하여 사용할 수 있다.

 

하지만 위의 코드를 보면 겹치는 부분이 존재한다. 이부분이 거슬리지 않는가?

 

따라서 부모클래스와 자식 클래스가 공유하는 메서드 내의 코드의 중복이 있을 때 사용할 수 있는 함수가 있는 데 이 함구가 바로 super() 이다.

 

 

 


 

 

 2  super()

 

 

자식클래스가 부모클래스의 메서드를 호출할 경우 사용한다.

 

super().메서드()

 

 

# 다음 좌표를 구하는 예제를 통해 간단하게 super()를 사용해보자. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Point:
     def __init__(self,x,y):
          self.x = x
          self.y = y
 
     def draw(self):
          print("x >",self.x)
          print("y >",self.y)
# x,y,z
class Point3D(Point):
     def __init__(self,x,y,z):
          self.x = x
          self.y = y
          self.z = z
 
     def draw(self):    # 메서드 오버라이딩 : 메서드 내용을 재정의 해줬다.
          super().draw() # super() : 부모클래스 내의 요소 호출
          print("z >",self.z)
 
c1 = Point3D(10,10,10)
c1.draw()
cs
x > 10
y > 10
z > 10
>>>

 

줄1에서 부모클래스를 생성하였고 생성자를 통해 x,y값을 받는다. 그리고 draw() 함수를 통해 x와 y 값을 출력한다.

 

줄10에서 이를 상속받는 클래스 Point3D를 생성해주고 생성자에 z 값을 하나 더 받아오도록 설정한다.

그리고 draw() 메서드 안에 super().draw() 를 사용하여 부모클래스 draw() 메서드의 요소 ( print("x >",self.x) / print("y >",self.y) ) 를 호출해준다.

그리고 줄18의 내용을 추가해주어 draw() 메서드를 오버라이딩 해주었다.

 

그리고 줄20 에서 Point3D 인스턴스를 생성해주고 draw()를 호출해주어 위와 같은 결과가 나왔다.

 

 

 

 

위의 예제는 부모클래스의 메서드를 호출하는 방법이었다.

 

 

 


 

 

# 다음 예제로 알아볼 내용은 부모클래스의 생성자를 호출하는 방법이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class A:
     def __init__(self):
          print("a")
 
class B(A):
     def __init__(self):
          print("b")
 
class C(B):
     def __init__(self):
          super().__init__()  # 부모의 생성자를 호출
          print("c")
 
c1 = C()
cs

위의 C 클래스는 B 클래스를 상속받고 B 클래스는 A클래스를 상속받는다. 그리고 줄10에서 C클래스의 생성자는 줄11에서 부모클래스인 B의 생성자를 호출하고나서 줄12를 실행하는 구조이다.

C그렇다면 c1객체는 어떤 값을 출력할까? 

 

먼저 줄14의 c1객체는 C클래스의 인스턴스이다. 따라서 줄9부터 실행, 줄10의 C클래스 생성자 안으로 들어가 실행하게 되고 줄11의 구문은 부모클래스의 생성자를 호출한 것으로 해당 구문은 줄6의 내용을 호출 한 것으로 즉 B클래스의 생성자내부로 올라간다. 따라서 줄7의 내용을 출력하고 생성자구문이 끝났기 때문에 다시 내려와 줄12의 구문을 실행할 것이다.

 

따라서 출력결과는 다음과 같이 나온다. 

b
c

 

 

 


 

 

 

 

# 그리고 위 예제의 상속 구조를 알아보기 위하여 mro()함수를 사용해보자.

mro() 함수는 클래스의 상속 구조를 탐색하고 싶을 경우 사용하는 내장함수이다. 클래스의 탐색 순서를 알려준다.

따라서 위의 코드에 다음 코드를 추가 후 실행시켜준다.

print(C.mro())    # C클래스의 상속구조 탐색

 

 

실행결과 다음과 같이 출력한다. C클래스는 B클래스를 상속받고 B클래스는 A클래스를 상속받는 구조이다. 그런데 object라고 하는 클래스는 어디에서 온걸까?

[<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
>>>

 

 

파이썬을 포함한 모든 언어들은 상속의 최상위에 항상 object 클래스가 존재한다. 따라서 모든 클래스들은 object 클래스를 상속받고 mro()로 클래스의 상속을 살펴보면 최상단에 object 클래스가 존재한다.

 

 

 

 


 

 

 

# 다음은 부모클래스의 생성자에 self 외의 매개변수가 있을 경우 오버라이딩 하는 예제이다.

위의 예제에서 클래스 B에 매개변수 a를 추가해준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class A:
     def __init__(self):
          print("a")
 
class B(A):
     def __init__(self,a):
          print("b",a)
 
class C(B):
     def __init__(self):
          super().__init__(10)  # 부모클래스에서 물려받은 생성자 매개변수를 넘겨줘야 한다.
          print("c")
          
c1 = C()
cs

 

 

위와 같이 부모클래스의 생성자에 매개변수가 존재한다면 자식클래스의 생성자에서 super()로 부모클래스 생성자를 호출할 경우 매개변수를 넘겨줘야 한다. 따라서 출력 결과는 다음과 같이 나온다.

 

b 10
c
>>>

 

 

 

 

 

 

 

 


 

 

Ex 예제

# 사각형 클래스 (매개변수가 있는 부모클래스 생성자를 호출)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Shape:
     def __init__(self,x,y):
          self.x = x
          self.y = y
          print("부모")
 
     def draw(self):
          pass
 
class Rect(Shape):
     def __init__(self,x,y):
          super().__init__(x,y) # super()
          print("사각형")
 
     # 오버라이딩
     def draw(self):
          print("사각형그린다.")
          print("가로>",self.x)
          print("세로>",self.y)
 
r1 = Rect(10,10)
r1.draw()
 
cs
부모
사각형
사각형그린다.
가로> 10
세로> 10
>>>

 

줄1에서 부모클래스 정의 후 줄10, 자식클래스 Rect에서 부모클래스의 생성자를 호출하였다. 부모클래스의 생성자에 매개변수가 있으므로 호출시 매개변수를 입력해주었다. 

 

그리고 draw() 함수에 줄17~19의 내용을 추가해 오버라이딩해주었다.

 

main()에서 r1객체생성 과 동시에 매개변수를 입력해준 후 draw()를 호출해주면 위와 같은 결과가 나온다.

 

 

 

 

 

 


 

 

Ex 예제

# CellPhone(상위클래스) 를 이용하여 두개의 하위클래스 ( D_caPhone , Mp3Phone )를 생성한 후 하위 클래스의 생성자를 이용하여 각 멤버변수를 초기화한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
class CellPhone:
     def __init__(self,model="무명폰",number="",chord=5): # 매개변수 초기화
          self.model = model
          self.number = number
          self.chord = chord
 
     def setNumber(self,setNumber):
          self.number = setNumber
 
     def getModel(self):
          print('모델명> ',end="")
          return self.model
 
     def getChord(self):
          print('화음> ',end="")
          return self.chord
 
     def getNumber(self):
          print('번호> ',end="")
          return self.number
 
class D_caPhone(CellPhone):
     def __init__(self,model,number,chord,pixel):
          super().__init__(model,number,chord)    # super()
          self.pixel = pixel
          print("디카폰!")
 
     def getPixel(self):
          print('해상도> ',end="")
          return self.pixel
          
class Mp3Phone(CellPhone):
     def __init__(self,model,number,chord,size):
          super().__init__(model,number,chord)    # super()
          self.size = size
          print("mp3폰!")
 
     def getSize(self):
          print('크기> ',end="")
          return self.size
 
     def getNumber(self):     # 메서드 오버라이딩
          print("MP3폰 안녕~",self.number)
          return self.number
 
b1 = D_caPhone("디카폰.LG","010-1234-1234",5,"500px")
m1 = Mp3Phone("mp3폰.samsung","010-345-345",5,10)
 
print()
print(b1.getModel())
print(b1.getChord())
print(b1.getNumber())
print(b1.getPixel())
 
print()
print(m1.getModel())
print(m1.getChord())
print(m1.getNumber())
print(m1.getSize())
 
print()
m1.setNumber("011-2345")
print(m1.getNumber())
cs
디카폰!
mp3폰!

디카폰.LG
화음> 5
010-1234-1234
500px

mp3폰.samsung
화음> 5
MP3폰 안녕~ 010-345-345
010-345-345
10

MP3폰 안녕~ 011-2345
011-2345
>>>

 

 

줄2에서 부모클래스의 생성자의 매개변수를 통해 모델명과 전화번호, 화음을 입력받고 그 값을 변수로 저장했다. main()에서 인수를 입력하지 않을 경우를 대비해 매개변수들을 모두 초기화 해주었다. 

그리고 줄7에서 전화번호 변경 기능의 메서드를 작성하고 줄10, 14, 18에서 모델명, 전화번호, 화음을 저장한 값을 반환하는 메서드를 작성하였다.

그리고 줄22에서 CellPhone 클래스를 상속받는 D_caPhone 클래스를 생성하였고 생성자의 호출을 통해 모델명과 전화번호, 화음을 입력받고 해상도를 추가로 입력받아서 변수 pixel에 저장해주었다.

pixel값을 입력받았기 때문에 줄28에서 이 pixel값을 반환하는 메서드 getPixel()도 작성해주었다.

그리고 줄32에서 CelloPhone을 상속받는 Mp3Phone클래스를 만들어주고 D_caPhone 클래스와 똑같이 부모클래스의 생성자를 호출하고 여기서는 size 값을 추가로 입력받았다.

그리고 줄38에서 size 값을 입력받았기 때문에 이는 반환하는 메서드 getSize를 작성해주었다.

또한 줄42에서는 getNumber메서드 오버라이딩으로 메서드내의 내용을 수정해주었다. 따라서 main의 출력결과 위와같이 나오는 것을 확인할 수 있다.

줄62에서 m1객체의 setNumber()메서드를 사용하여 전화번호의 값을 변경해주었고 이를 출력한 결과 변경해준 값으로 번호가 저장된 것을 확인할 수 있다.

반응형

댓글