- 프로그램을 제대로 만들기 위해 알아야 하는
예외 처리
와파일 다루기
에 대해서 공부- 대부분의 경우 우리가 만드는 프로그램에는 우리가 예상치 못한 오류가 생기곤 하는데, 이를 해결하기 위해서 사전에 모든 경우를 다 지정해서 대비하는 경우도 있지만, 특정 상황에서 발생할 수 있는 예외를 포괄적으로 지정해서 대비하는 경우도 있으며, 실제 프로그램을 작성할 때는 파일에 존재하는 데이터를 사용하는 경우가 많음
- 프로그램을 진행하면서 기록을 남기는
로깅
에 대해서도 배움
로그 데이터
를 남기는 건 오늘날 데이터 기반의 애플리케이션 개발에서 매우 중요한 이슈가 되고 있으며, 로그 데이터를 설계하는 것이 하나의 분야로 자리잡고 있을 만큼 관심을 받고 있음
- 주소를 입력하지 않고 배송 요청
- 저장도 안 했는데 컴퓨터 전원이 나감
- 게임 아이템 샀는데 게임에서 튕김
→ 예상치 못한 많은 일(예외) 들이 생김
- 예상 가능한 예외
- 발생 여부를 사전에 인지할 수 있는 예외
- 사용자의 잘못된 입력, 파일 호출 시 파일 없음
- 개발자가 반드시
명시적으로 정의
해야 함
- 예상이 불가능한 예외
- 인터프리터 과정에서 발생하는 예외, 개발자 실수
- 리스트의 범위를 넘어가는 값 호출, 정수 0으로 나눔
- 수행 불가 시,
인터프리터가 자동 호출
- 예외가 발생할 경우 후속 조치 등 대처 필요
- 없는 파일 호출 → 파일없음을 알림
- 게임이상 종료 → 게임정보 저장
프로그램 = 제품, 모든 잘못된 상황에 대처가 필요 → Exception Handling
try ~ except 문법
try: # 예외 발생 가능 코드 except <Exception Type>: # 예외 발생시 대응하는 코드
0으로 숫자를 나눌 때 예외처리 하기
for i in range(10): try: print(10 / i) except ZeroDivisionError: print("Not divided by 0")
- 출력 예시
Built-in Exception : 기본적으로 제공하는 예외
예외정보 표시하기
for i in range(10): try: print(10 / i) except ZeroDivisionError as e: print(e) print("Not divided by 0")
- 단, 다른 사용자가 어떤 오류인지 모르므로, 전체 Exception은 좋은 코드가 아님
try ~ except ~ else
: 예외가 발생하지 않을 때 동작하는 코드try: 예외 발생 가능 코드 except <Exception Type>: 예외 발생시 동작하는 코드 else: 예외가 발생하지 않을 때 동작하는 코드
for i in range(10): try: result = 10 / i except ZeroDivisionError: print("Not divided by 0") else: print(10 / i)
try ~ except ~ finally
: 예외 발생 여부와 상관없이 실행됨try: 예외 발생 가능 코드 except <Exception Type>: 예외 발생시 동작하는 코드 finally: 예외 발생 여부와 상관없이 실행됨
try: for i in range(1, 10): result = 10 // i print(result) except ZeroDivisionError: print("Not divided by 0") finally: print("종료되었습니다.")
- 필요에 따라
강제로 Exception을 발생
raise <Exception Type>(예외정보) while True: value = input("변환할 정수 값을 입력해주세요") for digit in value: if digit not in "0123456789": raise ValueError("숫자값을 입력하지 않으셨습니다") print("정수값으로 변환된 숫자 -", int(value))
특정조건에 만족하지 않을 경우
예외 발생assert 예외조건 def get_binary_nmubmer(decimal_number): assert isinstance(decimal_number, int) return bin(decimal_number) print(get_binary_nmubmer(10))
- File system, 파일 시스템, OS에서 파일을 저장하는 트리구조 저장 체계
File from wiki
: 컴퓨터 등의 기기에서 의미 있는 정보를 담는 논리적인 단위로 모든 프로그램은 파일로 구성되어 있고, 파일을 사용
파일의 기본 체계 – 파일 vs 디렉토리
디렉토리 (Directory)
폴더
또는 디렉토리로 불림- 파일과 다른 디렉토리를 포함할 수 있음
파일 (File)
- 컴퓨터에서 정보를 저장하는 논리적인 단위 (wikipedia)
- 파일은 파일명과 확장자로 식별됨 (예: hello.py)
- 실행, 쓰기, 읽기 등을 할 수 있음
파일의 종류
- 기본적인 파일 종류로text 파일
과binary 파일
로 나눔
- 컴퓨터는text 파일을 처리하기 위해
binary 파일로 변환
시킴 (예: pyc파일)
- 모든 text 파일도 실제는 binary 파일
- ASCII/Unicode 문자열 집합으로 저장되어 사람이 읽을 수 있음
- 파이썬은 파일 처리를 위해
“open”
키워드를 사용함f = open("<파일이름>","접근 모드") f.close()
read() txt 파일 안에 있는 내용을 문자열로 반환
f = open("i_have_a_dream.txt","r" ) contents = f.read() print(contents) f.close()
with 구문
과 함께 사용하기with open("i_have_a_dream.txt","r") as my_file: contents = my_file.read() print (type(contents), contents)
한 줄씩 읽어 List Type으로 반환
with open("i_have_a_dream.txt","r") as my_file: content_list = my_file.readlines() #파일 전체를 list로 반환 print(type(content_list)) #Type 확인 print(content_list) #리스트 값 출력
실행 시 마다 한 줄 씩 읽어오기
with open("i_have_a_dream.txt", "r") as my_file: i = 0 while True: line = my_file.readline() if not line: break print (str(i) + " === " + line.replace("\n","")) #한줄씩 값 출력 i = i + 1
단어 통계 정보 산출
with open("i_have_a_dream.txt","r") as my_file: contents = my_file.read() word_list = contents.split(" ") #빈칸 기준으로 단어를 분리 리스트 line_list = contents.split("\n") #한줄 씩 분리하여 리스트 print("Total Number of Characters :", len(contents)) print("Total Number of Words:", len(word_list)) print("Total Number of Lines :", len(line_list))
mode는 “w”, encoding=“utf8”
f = open("count_log.txt", mode="w", encoding="utf8") for i in range(1, 11): data = "%d번째 줄입니다.\n" % i f.write(data) f.close()
mode는 “a”는 추가 모드
with open("count_log.txt",mode="a", encoding="utf8") as f: for i in range(1, 11): data = "%d번째 줄입니다.\n" % i f.write(data)
- os 모듈을 사용하여
Directory 다루기
import os os.mkdir("log")
- Directory가
있는지 확인
하기if not os.path.isdir("log"): os.mkdir("log")
- 최근에는
pathlib 모듈
을 사용하여path를 객체
로 다룸
>>> import pathlib >>> >>> cwd = pathlib.Path.cwd() >>> cwd WindowsPath('D:/workspace') >>> cwd.parent WindowsPath('D:/') >>> list(cwd.parents) [WindowsPath('D:/')] >>> list(cwd.glob("*")) [WindowsPath('D:/workspace/ai-pnpp'), WindowsPath('D:/workspace/cs50_auto_grader'), WindowsPath('D:/workspace/data-academy'), WindowsPath('D:/workspace/DSME-AI-SmartYard'), WindowsPath('D:/workspace/introduction_to_python_TEAMLAB_MOOC'), ```
1) Directory가 있는지, 2) 파일이 있는지 확인 후
import os if not os.path.isdir("log"): # isdir 대신 exist도 쓸 수 있음 os.mkdir("log") if not os.path.exists("log/count_log.txt"): f = open("log/count_log.txt", 'w', encoding="utf8") f.write("기록이 시작됩니다\n") f.close() with open("log/count_log.txt", 'a', encoding="utf8") as f: import random, datetime for i in range(1, 11): stamp = str(datetime.datetime.now()) value = random.random() * 1000000 log_line = stamp + "\t" + str(value) +"값이 생성되었습니다" + "\n" f.write(log_line)
- 파이썬의 객체를
영속화(persistence)하는 built-in 객체
- 데이터, object 등 실행중 정보를 저장 →
불러와서 사용
- 저장해야하는 정보, 계산 결과(모델) 등 활용이 많음
import pickle f = open("list.pickle","wb") test = [1, 2, 3, 4, 5] pickle.dump(test, f) f.close()
메모리에서 삭제
f = open("list.pickle","rb") test_pickle = pickle.load(f) print(test_pickle) f.close()
rb를 사용해서 저장된걸 다시 불러와 사용
삭제 후 다시 rb로 불러오는 예시코드
import pickle class Mutltiply(object): def__init__(self, multiplier): self.multiplier = multiplier def multiply(self, number): return number * self.multiplier muliply = Mutltiply(5) muliply.multiply(10) f = open("multiply_object.pickle","wb") pickle.dump(muliply, f) f.close()
```python f = open("multiply_object.pickle","rb") multiply_pickle = pickle.load(f) multiply_pickle.multiply(5) ```
- 프로그램이 실행되는 동안 일어나는 정보 기록 남기기
- 유저의 접근, 프로그램의 Exception, 특정 함수의 사용
- Console 화면에 출력, 파일에 남기기, DB에 남기기 등
- 기록된 로그를 분석하여 의미있는 결과를 도출 할 수 있음
- 실행시점에서 남겨야 하는 기록, 개발시점에서 남겨야하는 기록
- 기록을 print로 남기는 것도 가능함
- 그러나 Console 창에만 남기는 기록은 분석 시 사용불가
- 때로는 레벨별(개발, 운영)로 기록을 남길 필요도 있음
- 모듈별로 별도의 logging을 남길 필요도 있음
- 이러한 기능을 체계적으로 지원하는 모듈이 필요함
Python의 기본 Log 관리 모듈
import logging logging.debug("틀렸잖아!") logging.info("확인해") logging.warning("조심해!") logging.error("에러났어!!!") logging.critical ("망했다...")
- 프로그램 진행 상황에 따라 다른 Level의 Log를 출력함
- 개발 시점, 운영 시점마다 다른 Log가 남을 수 있도록 지원함
DEBUG > INFO > WARNING > ERROR > Critical
- Log 관리 시 가장 기본이 되는 설정 정보
import logging logger = logging.getLogger("main") stream_hander = logging.StreamHandler() logger.addHandler(stream_hander) >> logger.setLevel(logging.DEBUG) logger.debug("틀렸잖아!") logger.info("확인해") logger.warning("조심해!") logger.error("에러났어!!!") logger.critical("망했다...") >> logger.setLevel(logging.CRITICAL) logger.debug("틀렸잖아!") logger.info("확인해") logger.warning("조심해!") logger.error("에러났어!!!") logger.critical("망했다...")
- 프로그램의
실행 설정을 파일에 저장
Section, Key, Value 값의 형태
로 설정된 설정 파일을 사용- 설정파일을
Dict Type
으로 호출 후 사용import configparser config = configparser.ConfigParser() config.sections() config.read('example.cfg') config.sections() for key in config['SectionOne']: print(key) config['SectionOne']["status"] # example.cfg ''' [SectionOne] Status: Single Name: Derek Value: Yes Age: 30 Single: True [SectionTwo] FavoriteColor = Green [SectionThree] FamilyName: Johnson '''
- Console 창에서 프로그램 실행 시 Setting 정보를 저장함
- 거의 모든 Console 기반 Python 프로그램 기본으로 제공
- 특수 모듈도 많이 존재하지만(TF), 일반적으로 argparse를 사용
Command-Line Option
이라고 부름
import argparse parser = argparse.ArgumentParser(description='Sum two integers.') parser.add_argument('-a', "--a_value", dest=”A_value", help="A integers", type=int) parser.add_argument('-b', "--b_value", dest=”B_value", help="B integers", type=int) args = parser.parse_args() print(args) print(args.a) print(args.b) print(args.a + args.b)
def main(): parser = argparse.ArgumentParser(description='PyTorch MNIST Example') parser.add_argument('--batch-size', type=int, default=64, metavar='N', help='input batch size for training (default: 64)') parser.add_argument('--test-batch-size', type=int, default=1000, metavar='N', help='input batch size for testing (default: 1000)') parser.add_argument('--epochs', type=int, default=10, metavar='N', help='number of epochs to train (default: 10)') parser.add_argument('--lr', type=float, default=0.01, metavar='LR', help='learning rate (default: 0.01)') parser.add_argument('--momentum', type=float, default=0.5, metavar='M', help='SGD momentum (default: 0.5)') parser.add_argument('--no-cuda', action='store_true' , default=False, help='disables CUDA training') parser.add_argument('--seed', type=int, default=1, metavar='S', help='random seed (default: 1)’) parser.add_argument('--save-model', action='store_true' , default=False, help='For Saving the current Model') args = parser.parse_args() if __name__ == '__main__': main()
- Log의 결과값의
format을 지정
해줄 수 있음formatter = logging.Formatter('%(asctime)s %(levelname)s %(process)d %(message)s') 2018-01-18 22:47:04,385 ERROR 4410 ERROR occurred 2018-01-18 22:47:22,458 ERROR 4439 ERROR occurred 2018-01-18 22:47:22,458 INFO 4439 HERE WE ARE 2018-01-18 22:47:24,680 ERROR 4443 ERROR occurred 2018-01-18 22:47:24,681 INFO 4443 HERE WE ARE 2018-01-18 22:47:24,970 ERROR 4445 ERROR occurred 2018-01-18 22:47:24,970 INFO 4445 HERE WE ARE
logging.config.fileConfig('logging.conf') logger = logging.getLogger()
- 출력예시
[logger.info](http://logger.info/)('Open file {0}'.format("customers.csv",)) try: with open("customers.csv", "r") as customer_data: customer_reader = csv.reader(customer_data, delimiter=',', quotechar='"') for customer in customer_reader: if customer[10].upper() == "USA": #customer 데이터의 offset 10번째 값 [logger.info](http://logger.info/)('ID {0} added'.format(customer[0],)) customer_USA_only_list.append(customer) #즉 country 필드가 “USA” 것만 except FileNotFoundError as e: logger.error('File NOT found {0}'.format(e,))