Python

[Python] Part.6 상속과 예외처리

c1oud9 2023. 2. 15. 01:13

 

 

 

 

 

ch06-01. 상속

 

클래스의 계층구조

 

상속이란?

클래스의 상속이란 한 클래스가 다른 클래스로부터 데이터 속성과 메서드를 물려받는 것.

상속하는 클래스를 기반(base) 클래스 또는 상위(super) 클래스라고 하고 상속 받는 클래스를 파생(derived) 또는 하위(sub) 클래스라고 한다.

 

 

파이썬에서 상속 하는 법

class A:
  def 함수1 (self, 변수1 , 변수2 , ...):
   return 결과 

class B(A):\
   def 함수2 (self, 변수1 , 변수2 , ...):
     return 결과 

=> B 는 아래와 같음 . 

class B:
  def 함수1 (self, 변수1 , 변수2 , ...):
   return 결과 
  def 함수2 (self, 변수1 , 변수2 , ...): 
    return 결과

 

예시

class add_calculator :
  def addition (self, x , y):
    return x + y 
    
class good_calculator (add_calculator):
  def substraction (self, x , y):
    return x - y

_
calc1=add_calculator()
_
calc1.addition(3, 4)

# result
7
_
calc2 = good_calculator()
_
calc2.addition(3, 4)

# result
7
_
calc2.substraction(4, 3)

# result
1

 

부모의 기능을 불러오려면

super() 이용하면 부모에게 정의된 함수를 불러올 수 있다. 

class add_calculator: 
  def addition(self, x, y): 
    return x+y 
class good_calculator(add_calculator): 
  def addition(self, x, y): 
    print(super().addition(x,y))  # 부모의 addition 메소드를 불러와서 print(x+y)가 될 것이다.
    print('또?') 
    print(super().addition(x,y))
    
_
calc2.addition(3, 4)
    
# result
7
또?
7

 

overriding

만약에, 부모의 메소드와 이름이 똑같은 메소드를 자식에게 정의하면?

class A:
   def method(self, 변수):
     결과
        
class B(A):
   def method(self, 변수):
     다른 결과
        
A의 메소드는 무시되고 B는 새롭게 정의된 메소드를 사용!

 

다중 상속

class A:
    def 함수(self):
        결과
        
class B:
    def 함수(self):
        결과
        
class C(A,B):
    pass

위와같은 상황에서 C는 A, B중에 어떤 함수를 상속받을까?
=> 먼저 입력한 A의 함수를 상속받는다.

 

 


 

 

ch06-02. 추상 클래스와 isinstance

 

추상 클래스

  • 추상 클래스는 abc 모듈의 ABCMeta 클래스를 상속받아 만든다.
  • 추상 클래스는 자신의 객체를 생성할 수 없다.
  • 추상 메소드라는 @abstractmethod 데코레이터를 사용하여 자신의 하위객체에게 특정 메소드의 생성을 강제할 수 있다.
  • 추상 메소드는 이름만 존재하고 내용은 없다.
  • 추상 클래스는 abc 모듈의 ABCMeta 클래스를 상속받아 만든다. 이때 반드시 metaclass=메타클래스이름 의 형태로 상속받는다.
  • 추상 클래스는 자신의 객체를 생성할 수 없다.
  • 추상 메소드라는 @abstractmethod 데코레이터를 사용하여 자신의 하위객체에게 특정 메소드의 생성을 강제할 수 있다.
  • 추상 메소드는 이름만 존재하고 내용은 없다
from abc import *
class Abstract(metaclass = ABCMeta):
  @abstractmethod
  def method(self):
    pass

class test(Abstract):     # 이 경우 abstract method인 method를 정의하지 않아서 오류발생
  pass

_
test1 = test()

# result
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-21-b98d2860ac1d> in <module>
----> 1 test1 = test()

TypeError: Can't instantiate abstract class test with abstract methods method
from abc import *
class Abstract(metaclass = ABCMeta):
  @abstractmethod
  def method(self):
    pass

class test(Abstract):     # abstract method인 method를 정의 오류발생 안함
  def method(self):
    pass 

_
test1 = test()

# result
error 없음

 

 

isinstance

isinstance(변수, 클래스이름)은 주어진 변수의 값이 클래스의 인스턴스인지 여부를 반환해 주는 함수!

a = 3
_
isinstance(a, int)

# result
True
_
isinstance(test1, test)

# result
True
_
isinstance(test1, int)

# result
False

 

 


 

 

ch06-03. 예외 처리

 

Try ~ except

try:
  에러 문장
except:
  에러 났을 때 실행할 문장
    
위와 같이 입력할 경우 에러 문장에서 에러가 발생했을 때 except에 있는 문장이 실행된다.
try:
  에러 문장
except Exception as e: # 이렇게 as e라고 적어 놓으면 발생할 에러문이 string의 형태로 e에 저장!
  print(e)
  에러 났을 때 실행할 문장
    
위와 같이 입력할 경우 에러 문장에서 에러가 발생했을 때 except에 있는 문장이 실행된다.
try:
  에러 문장
except:
  에러 났을 때 실행할 문장
else:
  에러가 안 나면 실행할 문장
finally:
  에러 발생 여부와 상관없이 실행될 문장

 

예외의 강제 발생

raise Exception(“강제로 에러를 발생시킴“)

과 같은 방식으로 raise문을 통해 우측의 string을 출력하며 에러가 발생하게 할 수 있습니다.

 

 

예시

try:
  1/0
except:
  print('에러')


# result
에러
try:
  1/0
except Exception as e:
  print(e)
  print('에러')
  

# result
division by zero
에러
raise Exception('내가 만든 에러')


#result
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
<ipython-input-31-04b2f0b9af1e> in <module>
----> 1 raise Exception('내가 만든 에러')

Exception: 내가 만든 에러
a = 3
assert a>=10, '값이 너무 작습니다.'


# result
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-32-0cce7c5f52e1> in <module>
      1 a = 3
----> 2 assert a>=10, '값이 너무 작습니다.'

AssertionError: 값이 너무 작습니다.


_


a = 13
assert a>=10, '값이 너무 작습니다.'


#result
error 없음

 

 


 

 

ch06-04. 실전 문제풀이

# 추상클래스는 ABCMeta를 상속 받고 추상메소드가 있어야함
from abc import ABCMeta, abstractmethod
class Abstract1 (metaclass=ABCMeta):
  attr = '추상클래스'
  @abstractmethod
  def m1(self):
    pass
  def m2(self):
    print('대박')
# 추상클래스는 객체를 못 만듬 -> error 나게 된다.
a1 = Abstract1()


# result
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-1-da8e9c6b50f5> in <module>
      9     print('대박')
     10 # 추상클래스는 객체를 못 만듬
---> 11 a1 = Abstract1()

TypeError: Can't instantiate abstract class Abstract1 with abstract methods m1
from abc import *
class A (metaclass = ABCMeta):
  def prn(self):
    print('대박')
  @abstractmethod
  def p2(self):
    pass
# 추상클래스를 상속 받은 클래스는 반드시 추상메소드를 재정의
class A1(A):
  def aa(self):
    print('사건')
  def p2(self):
    print('아 이건 필수네')
a1 = A1()
a1.aa()
a1.p2()
a1.prn()


# result
사건
아 이건 필수네
대박
# 추상클래스
from abc import *
class Animal(metaclass = ABCMeta):
  def __init__(self,eat1='그냥'):
    self.eat1 = eat1
  def eat(self): print('먹어야 산다')
  # 추상메서드 상속받은 자식이 재정의 해야 한다
  @abstractmethod
  def move(self): pass
class Bird(Animal):
  def move(self):
    print('하늘을 난다 {}'.format(self.eat1))
class Person(Animal):
  def move(self): print('두발로 걷는다 {}'.format(self.eat1))
class Fish(Animal):
  def move(self): print('지느러미로 헤엄친다')
b1 = Bird();  p1 = Person('대박');  f1 = Fish()
animal = [b1, p1, f1]
for an in animal:
  print('====================')
  an.eat(); an.move()
  

# result
====================
먹어야 산다
하늘을 난다 그냥
====================
먹어야 산다
두발로 걷는다 대박
====================
먹어야 산다
지느러미로 헤엄친다
# 클래스 상속
class A:
  def m1(self):
    print('점심시간')

class B(A):
  def m2(self):
    print('1차 프로젝트 종료일')

class C(B):
  def m3(self):
    print('뭘 그래')
    
c1 = C()
c1.m1(); c1.m2(); c1.m3()


# result
점심시간
1차 프로젝트 종료일
뭘 그래
# 클래스 상속
class A:
  def m1(self):
    print('점심시간')

class B(A):
  def m2(self):
    print('1차 프로젝트 종료일')

class C(B):
  def m3(self):
    super().m2()
    print('뭘 그래')
    
c1 = C()
c1.m1(); c1.m2(); c1.m3()


# result
점심시간
1차 프로젝트 종료일
1차 프로젝트 종료일
뭘 그래
class Call:
  # __call__ : 객체를 메서드처럼 호출할 때 실행
  def __call__(self, *a):
    print('대박이야 :')
    for i in a:
      print(i)
c1 = Call()
c1()
c1('뭐지', '허걱', '허각', '허공')


# result
대박이야 :
대박이야 :
뭐지
허걱
허각
허공
while True:
  try:
    li = [1, 2, 3]
    print('인덱스 번호를 입력하세요')
    num1 = input()
    if num1 == 'x': break
    num = int(num1)
    print('{} / 2 = {}'.format(li[num], li[num]/2))
  except Exception as err:
    print('에러다 {}'.format(err))
  else:   #에러가 발생하지 않았을 때 할 실행문
    print('잘했어 친구')
  finally:    # 에러 발생여부와 관계 없이 항상 실행
    print('난 무조건이야!!')
    
    
# result
인덱스 번호를 입력하세요
2
3 / 2 = 1.5
잘했어 친구
난 무조건이야!!
인덱스 번호를 입력하세요
3
에러다 list index out of range
난 무조건이야!!
인덱스 번호를 입력하세요
x
난 무조건이야!!
try:  # 0 1 2 index3번째 데이터 없기 때문에
      # [1, 2, 3][3]
      # 'a'+1
  a=[1, 2, 3]
  a[4]
except TypeError:   print('1번 에러')
except ZeroDivisionError:   print('2번 에러')
except IndexError:    print('3번 에러')


# result
3번 에러
try:  # 0 1 2 index3번째 데이터 없기 때문에
      # [1, 2, 3][3]
      # 'a'+1
  4 / 0
except TypeError:   print('1번 에러')
except ZeroDivisionError:   print('2번 에러')
except IndexError:    print('3번 에러')


# result
2번 에러
try:  # 0 1 2 index3번째 데이터 없기 때문에
      # [1, 2, 3][3]
      # 'a'+1
  'a'+1
except TypeError:   print('1번 에러')
except ZeroDivisionError:   print('2번 에러')
except IndexError:    print('3번 에러')


# result
1번 에러
# Generator Expression
# 내부함수인 yieid를 수행
g1 = (n+n for n in range(21))
# print(list(g1))
for i in range(10):
  val = next(g1)
  print(val)
# 나머지 데이터 출력
# for i in g1:
#     print(i)


# result
0
2
4
6
8
10
12
14
16
18
def yourRange(start, end):
  current = start
  while current < end:
    yield current
    current +=1
  return

for i in yourRange(0, 5):
  print(i)


# result
0
1
2
3
4
class Base:
  def m1(self):
    print('대박')
class Derived(Base):  # 파이썬의 살속(?)은 ()
  def m2(self):
    print('사건')
if __name__=='__main__':
  b1 = Base();  b1.m1();  # b1.m2() # Base에 n2라는 메소드가 없음
  d1 = Derived(); d1.m1(); d1.m2()
  # Derived에 없는 메소드 n1()이 실행되는 이유는 상속 
  

# result
대박
대박
사건
# 1**3, 2**3,…, 10**3
li = [ n**n for n in range(1, 11)]
print(li)


# result
[1, 4, 27, 256, 3125, 46656, 823543, 16777216, 387420489, 10000000000]
class A:
  def m1(self):
    print('난 A')
class B:
  def m2(self):
    print('난 B')
class C:
  def m3(self):
    print('난 C')
class F(A, B, C):
  pass
f1 = F()
f1.m1(); f1.m2(); f1.m3()


# result
난 A
난 B
난 C
class MyException(Exception):   # Exception 또한 클래스
  def __init__(self, arg):
    super().__init__('정수가 아녀 : {}'.format(arg))
def conver(num):
  if num.isdigit():
    return int(num)
  else:
    raise MyException(num)
try:
  print('숫자를 입력하세요')
  num = input()
  a = conver(num)
except MyException as err:
  print('에러네 : {}'.format(err))
else:
  print('입력한 수 {} 타입 {}'.format(a, type(a)))


# result
숫자를 입력하세요
2.5
에러네 : 정수가 아녀 : 2.5
class A1:
  def m1(self):
    print('난 A1야')
# overriding은 부모와 매개변수와 이름이 같은 메서드
# 재정의, 현재 객체와 가까운 메서드 사용
class A2(A1):
  def m1(self):
    print('난 A2야')
class A3(A2):
  def m1(self):
    print('난 A3야')
a1 = A1();  a2 = A2();  a3 = A3()
a1.m1(); a2.m1(); a3.m1()


# result
난 A1야
난 A2야
난 A3야
try:
  print('숫자를 입력하세요')
  num = int(input())
  print('숫자 : ', num)
  # 무조건 에러 발생, 테스트 등에서 강제 종료
  raise Exception('에러야')
except Exception as err:
  print('에궁 {}'.format(err))
  

# result
숫자를 입력하세요
2
숫자 :  2
에궁 에러야
class Base:
  def __init__(self, name):
    print('Base.__init__()')
    self.message = '안뇽'
    print(name)
# super() 무모의 생성자를 가르킴
class Derived(Base):
  def __init__(self):
    print('Derived __init__()')
    super().__init__('승헌')
    #self.message 부모의 변수를 살속 받아서 사용
    print('메세지 : {}'.format(self.message))
d1 = Derived()


# result
Derived __init__()
Base.__init__()
승헌
메세지 : 안뇽
class A:
  def __init__(self, name, age):
    print('A__init__')
    self.name = name; self.age = age
class B(A):
  def __init__(self):
    print('B__init__')
    super().__init__('혜인', 15)
  def print(self):
    print('이름 : {}, 나이 : {}'.format(self.name, self.age))
b1 = B(); b1.print()


# result
B__init__
A__init__
이름 : 혜인, 나이 : 15
class add_calculator:
  def addition(self, x, y):
    return x+y
class good_calculator(add_calculator):
  def addition(self, x, y):
    print(super().addition(x, y))
    print('또?')
    print(super().addition(x, y))
    
_

calc1 = add_calculator()

_

calc1.addition(3, 4)

# result
7