본문 바로가기

카테고리 없음

[Python] 오버라이딩 / 추상클래스 / 예외처리 / 파일

 

이 글은 24.01.10에 본인 벨로그에 작성했던 글을 옮겨 온 것이다.

 

오버라이딩

  • 하위 클래스에서 상위 클래스의 메서드(함수)를 재 정의(override)하는 것.
    => 상속받은 클래스의 기능을 하위 클래스에서 다시 작성한다.
    => 중복된 코드를 제거하여 객체지향 프로그래밍 시 시간 절약도 가능

 

추상클래스(abstract class)

  • 미 구현된 메서드(=추상 메서드)을 한 개 이상 가지는 클래스를 의미한다.
  •  추상 클래스는, 자식 클래스에서 추상 메서드를 반드시 구현하도록 강제할 수 있다.
    1) 구체적으로 예를 들자면 "a라는, 선언만 되어 있고 그 외엔 구현되어있지 않은 메서드(기능)"를 갖고 있는 상위 "A"클래스를 하위 "B" 클래스가 상속받는다고 하자.
    2) 이 때, "A" 클래스는 "B"클래스로 하여금 "a"기능을 반드시 구현하도록(오버라이딩 하도록) 강제할 수 있다.
    3) 어떻게?? "B"클래스가 "A"클래스를 상속받았을 때 "a"기능을 구현하지 않으면 "B" 클래스로 객체 생성 시 에러가 나게 할 수 있다.
  • 예시 1) 자식 클래스에서 추상 메서드를 구현하지 않은 경우.
from abc import ABCMeta
from abc import abstractmethod  # 이 2가지를 import 하거나 from abc import * 해도 된다.

class plane(metaclass=ABCMeta): # 상위 클래스에 이와 같이 명시해준다.

    @abstractmethod     # 이 deco를 추가함으로써 flight 메서드를 추상 메서드로 정의한다.
    def flight(self):
        pass

    def go(self):
        print('go!')

    def back(self):
        print('back!')

class Airline(plane):

    def __init__(self, c):
        self.color = c

a_1 = Airline('red')
========================================
Traceback (most recent call last):
  File "C:\Users\quddu\Desktop\개인 공부\제로베이스\python\class 생성\object.py", line 20, in <module>
    a_1 = Airline('red')
          ^^^^^^^^^^^^^^
TypeError: Can't instantiate abstract class Airline with abstract method flight

이렇게, 객체 생성 시 에러가 뜬다.
에러가 난 코드와 에러 메시지를 보면, 추상 메서드를 정의하지 않아서 생긴 에러임을 알 수 있다.

 

  • 예시 2) 자식 클래스에서 추상 메서드를 구현 한 경우.
# 자식 클래스에서 추상 메서드를 구현 한 경우.

from abc import ABCMeta
from abc import abstractmethod  
class plane(metaclass=ABCMeta): 

    @abstractmethod     
    def flight(self):
        pass

    def go(self):
        print('go!')

    def back(self):
        print('back!')

class Airline(plane):

    def __init__(self, c):
        self.color = c

    def flight(self):
        print('짱 빠르다')

a_1 = Airline('red')

a_1.go()
a_1.back()
a_1.flight()
============================================
go!
back!
짱 빠르다
  • 그렇담, 이 추상 클래스는 어따 써먹는가?
    => 여러 하위 클래스가 하나의 상위 클래스를 상속받아 상속 클래스의 특정 기능(추상 메서드)을 각 하위 클래스의 용도에 맞게 구현해서 사용해야 하는 경우에 유용하게 쓰인다.

 

try~ except~ else

  • 예외 처리 구문이다.
    1) try : 에러가 발생할 것으로 예상되는 코드에 걸어줌
    2) except : try구문에서 에러가 발생했을 때 실행시킬 코드에 걸어줌
    3) else : try구문에서 에러가 발생하지 않았을 때 실행시킬 코드에 걸어줌.
  • 예시. 사용자로부터 5개의 수를 입력받아 짝수, 홀수, 실수로 구분해 각각을 리스트에 저장하는 프로그램 작성
even_list=[]
odd_list=[]
float_list=[]

n = 1

while n<6:  # 5개를 입력받아야 하니까.
    try:    # 입력된 것이 숫자인지 아닌지 걸러냄
        num = float(input('숫자를 입력하세요 : '))
    except:	# 숫자 외 다른 것이 입력되었을 때 아래 코드 실행.
        print('입력된 것은 숫자가 아닙니다. 다시 입력하세요.')
        continue
    else:   # 입력된 것이 숫자라면, 짝수 홀수 실수를 걸러냄.
        if num != round(num):   # 실수 필터링
            print('실수입니다')
            float_list.append(num)
        else:   # 짝, 홀수 필터링
            if num % 2 == 0:
                print('짝수입니다')
                even_list.append(int(num))
            else:
                print('홀수입니다')
                odd_list.append(int(num))
    n += 1

print(f'짝수 : {even_list}')
print(f'홀수 : {odd_list}')
print(f'실수 : {float_list}')
=========================================================
숫자를 입력하세요 : 1
홀수입니다
숫자를 입력하세요 : 2
짝수입니다
숫자를 입력하세요 : 3.14
실수입니다
숫자를 입력하세요 : 5
홀수입니다
숫자를 입력하세요 : ㅋㅋㅋ
입력된 것은 숫자가 아닙니다. 다시 입력하세요.
숫자를 입력하세요 : 6
짝수입니다
짝수 : [2, 6]
홀수 : [1, 5]
실수 : [3.14]

 

finally

  • 예외 발생 여부와 관계 없이 항상 실행한다.
    => 아래와 같은 경우에 사용한다
    => 외부 자원을 사용하는 경우, 작업 도중 예외가 있었던 없었던 작업이 끝나면 외부 자원 사용도 끝내야 할 것이다. 이러한 경우에 주로 사용한다.
even_list=[]
odd_list=[]
float_list=[]

n = 1

while n<6:  # 5개를 입력받아야 하니까.
    try:    # 입력된 것이 숫자인지 아닌지 걸러냄
        num = input('숫자를 입력하세요 : ')
        num = float(num)
    except:
        print('입력된 것은 숫자가 아닙니다. 다시 입력하세요.')
        continue
    else:   # 숫자라면, 짝수 홀수 실수를 걸러냄.
        if num != round(num):   # 실수 필터링
            print('실수입니다')
            float_list.append(num)
        else:   # 짝, 홀수 필터링
            if num % 2 == 0:
                print('짝수입니다')
                even_list.append(int(num))
            else:
                print('홀수입니다')
                odd_list.append(int(num))
    finally:
        print(f'입력된 데이터는 {num}입니다')
    n += 1

print(f'짝수 : {even_list}')
print(f'홀수 : {odd_list}')
print(f'실수 : {float_list}')
==========================================================
숫자를 입력하세요 : 1
홀수입니다
입력된 데이터는 1.0입니다
숫자를 입력하세요 : 2
짝수입니다
입력된 데이터는 2.0입니다
숫자를 입력하세요 : 3.14
실수입니다
입력된 데이터는 3.14입니다
숫자를 입력하세요 : ㅋㅋㅋ
입력된 것은 숫자가 아닙니다. 다시 입력하세요.
입력된 데이터는 ㅋㅋㅋ입니다
숫자를 입력하세요 : 4
짝수입니다
입력된 데이터는 4.0입니다
숫자를 입력하세요 : 5
홀수입니다
입력된 데이터는 5.0입니다
짝수 : [2, 4]
홀수 : [1, 5]
실수 : [3.14]

 

사용자 exception class

  • 직접 예외 클래스를 만들어 사용해보자
  • 예시 1)
class NoUseZero(Exception):     # NoUseZero라는 예외 클래스 생성
    def __init__(self, n):
        super().__init__(f'{n}으로는 나눌 수 없습니다.')

def div(n1,n2):
    if n2 == 0:     # n2가 0인 경우 NoUseZero 예외 클래스 raise
        raise NoUseZero(n2)
    else:           # 그 외의 경우는 나눗셈 수행
        print(f'{n1}/{n2} = {n1/n2}')

num1 = int(input('숫자 1 입력 : '))
num2 = int(input('숫자 2 입력 : '))

try:    # div()에서 에러가 발생할 것 같음...
    div(num1, num2)
except NoUseZero as e:  # 발생한다면? NoUseZero 출력.
    print(e)
==================================================================
숫자 1 입력 : 5
숫자 2 입력 : 8
5/8 = 0.625
----------------------
숫자 1 입력 : 6
숫자 2 입력 : 0
0으로는 나눌 수 없습니다.

 

  • 예시 2)
class PasswordLengthShortException(Exception):
    def __init__(self, str):
        super().__init__(f'{str}는 길이가 너무 짧습니다. 5자 이상 입력하세요')

class PasswordLengthLongException(Exception):
    def __init__(self, str):
        super().__init__(f'{str}는 길이가 너무 깁니다. 10자 이하로 입력하세요')

class PasswordWrongException(Exception):
    def __init__(self, str):
        super().__init__(f'{str}는 잘못된 암호입니다. 다시 입력하세요')

mypw = input('비밀번호를 입력하세요 : ')
original_pw = 'admin1234'

try:
    if len(mypw) < 5:       # len(mypw) < 5면 PasswordLengthShortException에러 발생시킴.
        raise PasswordLengthShortException(mypw)
    elif len(mypw) > 10:    # len(mypw) > 10이면 PasswordLengthLongException에러 발생시킴.
        raise PasswordLengthLongException(mypw)
    elif mypw == original_pw:   # mypw == original_pw면 accepted 출력
        print('accepted')
    elif mypw != original_pw:   # mypw != original_pw면 PasswordWrongException에러 발생시킴.
        raise PasswordWrongException(mypw)

except PasswordLengthShortException as e1:
    print(e1)   # PasswordLengthShortException 에러가 발생하면, 해당 메시지 출력
except PasswordLengthLongException as e2:
    print(e2)   # PasswordLengthLongException 에러가 발생하면, 해당 메시지 출력
except PasswordWrongException as e3:
    print(e3)   # PasswordWrongException 에러가 발생하면, 해당 메시지 출력
==========================================================================================
비밀번호를 입력하세요 : 1234
1234는 길이가 너무 짧습니다. 5자 이상 입력하세요
-----------------------------------------------------
비밀번호를 입력하세요 : 123456789000
123456789000는 길이가 너무 깁니다. 10자 이하로 입력하세요
-----------------------------------------------------
비밀번호를 입력하세요 : 123456
123456는 잘못된 암호입니다. 다시 입력하세요
-----------------------------------------------------
비밀번호를 입력하세요 : admin1234
accepted

 

텍스트 파일 열기

  • 파일 모드
    1) w(write): 파일을 쓸 때(덮어쓰기) 사용되고, 이 모드로 파일을 열 경우, 파일이 없으면 새로운 파일이 생성된다.
    2) a(append) : 파일을 쓸 때(덧붙이기) 사용되고, 이 모드로 파일을 열 경우, 파일이 없으면 새로운 파일이 생성된다.
    3) r(read) : 파일을 읽어올 때에만 사용되고, 이 모드로 파일을 열 경우, 파일이 없으면 에러가 발생한다.
    4) x(exclusive creation) : 파일 생성 및 쓸 때 사용된다. w 및 a 모드와의 차이점은 파일 생성 시 동명의 파일이 있으면 에러가 발생한다는 것. 즉, 다음과 같은 상황에서 유용하다.
    => (1) 파일을 새로 만들고 싶지만, 동명의 파일이 이미 있는 경우에는 에러를 내고 싶을 때
    => (2) 동명의 파일이 이미 있는지 일일이 확인하지 않고, 안전하게 새 파일을 만들고 싶을 때

예제)
특정 정수를 입력받아 2부터 해당 정수 사이의 모든 소수를 txt파일에 입력하고, 해당 파일의 내용을 읽어들이는 프로그램 작성.

def writePrineNum(n):
    file = open('C:/*****/*****/*******/prime_num.txt', 'a')
        # open('파일 경로', '모드') => 파일을 해당 모드로 연다
    file.write(str(n))
    file.write('\n')
        # 쓰기 모드로 열었으므로, write('쓸 내용')으로 텍스트를 쓴다.
    file.close()
        # open() 후에는 반드시 close()를 해 주어야 한다.

# 특정 정수까지의 소수를 모두 찾는 코드.
num = int(input('0보다 큰 정수 입력 : '))
for i in range(2, num+1):
    flag = True
    for j in range(2, i):
        if i % j == 0 :
            flag = False
            break
    if flag:
        writePrineNum(i)

# 텍스트 파일에 어떤 내용이 있는지 읽어들이는 'r' 모드.
file = open('C:/*****/*****/*******/prime_num.txt', 'r')
print(f'2부터 {num}까지의 소수는 \n{file.read()}')
file.close()
===============================================================
0보다 큰 정수 입력 : 50
2부터 50까지의 소수는 
2
3
5
7
11
13
17
19
23
29
31
37
41
43
47

with ~ as문

  • 파일을 연 후 close()를 생략할 수 있다.
    아래 예시와 같이 사용한다.
위에서 사용했던 소수를 찾아 txt파일에 입력하고, 읽어들이는 예제에
with ~ as 구문을 추가해보자.


def writePrineNum(n):
    file = open('C:/*****/*****/prime_num.txt', 'a')
    file.write(str(n))
    file.write('\n')
    file.close()

num = int(input('0보다 큰 정수 입력 : '))
for i in range(2, num+1):
    flag = True
    for j in range(2, i):
        if i % j == 0 :
            flag = False
            break
    if flag:
        writePrineNum(i)

# with ~ as 구문을 사용하면 close()를 해 주지 않아도 되어 코드가 간단해진다.
with open('C:/*****/*****/prime_num.txt', 'r') as r:
    print(f'2부터 {num}까지의 소수는 \n{r.read()}')
=============================================================
0보다 큰 정수 입력 : 50
2부터 50까지의 소수는 
2
3
5
7
11
13
17
19
23
29
31
37
41
43
47