클린파이썬 - 더 나은 함수 작성하기
ISBN 979-11-6175-5113
예제에 오타들이 엄청 많았다. 특히 예제코드에 오류가 너무 많아서 실망스러웠다. 목차만 봐도 좋은 내용들이 많았는데 번역도 아쉬움 😅 그렇지만 새로 알게된 내용들이 많아서 작성해본다!
1. 작은함수 단위
파이썬의 모든 것은 객체. 함수도 객체이다(파이썬에서 함수는 일급객체로써 다른 파이썬 자료형과 동일하게 취급된다. 예를들면 함수또한 다른 함수의 파라미터나 반환값이 될 수 있고 변수에 할당될 수 있다)
이때 함수는 하나의 작업을 담당해야한다.
어떻게 하나의 함수가 하나의 작업을 수행하는지 알 수 있는가?
- 이거는 사실 한 번에 알기 힘들다. 계속해서 코드를 읽고 이해하기 어려운 부분을 찾아내고 냄새나는 코드가 있는 곳을 뒤져야합니다..(aka
code smell
)
- 이거는 사실 한 번에 알기 힘들다. 계속해서 코드를 읽고 이해하기 어려운 부분을 찾아내고 냄새나는 코드가 있는 곳을 뒤져야합니다..(aka
import re
def get_unique_emails(file_name):
# 파일데이터 읽어 고유 이메일 저장
eamils = set()
with open(file_name) as file:
for line in file:
match = re.findall(r'[\w\.-]+@[\w\.-]+',line)
for email in match:
eamils.add(email)
return eamils
작은 단위의 함수처럼보이지만 get_unique_emails
는 현재 두 개의 일 하고 있다.
주어진 파일을 열고 각 줄을 반환하고
with open(file_name) as file:
각 줄에서 이메일형식 일치를 확인한다
match = re.findall(r'[\w\.-]+@[\w\.-]+',line)
따라서 위의 함수를 두 개로 나누어 본다면 아래가 된다.
def reed_file(file_name): # 주어진 파일을 열고 각 줄을 반환
with open(file_name) as file:
for line in file:
yield line
def get_unique_mail(file_name): # 파일데이터 읽어 고유 이메일 저장
eamils = set()
for line in reed_file(file_name):
match = re.findall(r'[\w\.-]+@[\w\.-]+',line)
for email in match:
eamils.add(email)
return eamils
reed_file
은 각 라인을 yield하는 포괄적함수get_unique_mail 고유한 이메일 가져온다
title: 하지만 처음부터 함수를 나누지 말고 먼저 기능 구현하고 이후 작동하면 함수를 나눈다!
2. 파일이 얼마나 큰지 모르면 제너레이터쓰자
제너레이터는 이터레이터 일종이다. (모든 이터레이터는 이터러블이다. 따라서 제너레이터 또한 이터러블이다)
next(제너레이터), 즉 next 입력으로 들어갈 수 있고 for loop의 입력으로 들어갈 수 있다.
일반 함수처럼 생겼지만 return 대신에 yield 사용한다.
def reed_file(file_name): # 주어진 파일을 열고 각 줄을 반환
with open(file_name) as file:
for line in file:
yield line
def get_unique_mail(file_name): # 파일데이터 읽어 고유 이메일 저장
eamils = set()
for line in reed_file(file_name): ######
match = re.findall(r'[\w\.-]+@[\w\.-]+',line)
for email in match:
eamils.add(email)
return eamils
여기에서 파일을 읽고
reed_file
의 반환값으로 제너레이터로 보내준이유는 파일의 크기가 얼마나 큰지 확실하지 않고 큰 파일을 처리할 때 list로 보내면 메모리가 부족할 가능성이 있기 때문이다제너레이터
가 함수 호출시 전체 함수가 아닌이터레이터
즉시 반환. 이 이터레이터를 통해 다른 작업 수행하고(리스트로 변경한다든가, regex작업 수행한다든가) 내장함수 next() 자동호출하고 yield 키워드 다음라인으로 돌아가 코드 가독성 좋다
- 파이썬은 데이터 반환시 데이터를 메모리에 저장하는데 제너레이터는 이문제 없음. 처리 할 데이터 많거나 사전에 데이터 크기 확실치 않은경우 제너레이터 사용
다만 모든 반환함수에 제너레이터 사용하라는건 아니고 특정데이터크기만 반호나해야하는거 사전에 안다면 리스트/튜플/집합/사전이 좋을 수도 있다
파이썬 제너레이터를 제대로 숙지한다. 제대로 사용하는 사람 없기 떄문에. 제너레이터는 코드를 명확하게 하고 메모리 문제 줄인다
3. None 반환 대신 예외발생
파이썬 동적언어니까 코드 작성시 예상치 못한 값 발견시 주의. None반환을 과도하게 사용하지 않는다. 따라서 예외를 발생시키자
- AS-IS
def read_lines_for_python(file_name, file_type):
if not file_name or file_type not in ("txt", "html"):
return None ################################
line = []
with open(file_name, "r") as file:
for line in file:
if "python" in line:
return "Found Python"
if not read_lines_for_python("python없는파일", "pdf"):
print("잘못된 파일 포맷 또는 파일 존재하지않음")
- TO-BE
def better_read_lines_for_python(file_name, file_type):
if file_type not in ("txt", "html"): ###########
raise ValueError("Not correct file format")
if not file_name:#################
raise IOError("File not found")
with open(file_name, "r") as file:
for line in file:
if "python" in line:
return "Found Python"
4. 명시적인 None 반환지양
- AS-IS : 함수의 결과가 None이나 int 의 값으로 예상되는데 소음이 너무 많기에 예외를 발생시키는게 낫다
def sumint(first,second):
if isinstance(first,int) and isinstance(second,int):
return first+second
else:
return None###########
sumint(1+"a")
- AS-IS
def sumint(first,second):
if isinstance(first,int) and isinstance(second,int):
return first+second
else:
raise ValueError("숫자만 입력하시오 ")##########
5. Default 및 키워드 인자 사용해 함수 호출
spam_emails(from="icehongssii@gmail",
to="kimchi123@gmail.com",
s="123")