Chap03 Organizing Your Code
1. formatting
1. 모든 상수값은 대문자로 사용하기
어디서 이 값을 가져왔는지 구별하기 위해 대문자로 선언하자
🙅♀️
seconds_in_a_day = 60 * 60 * 24
def display_uptime(uptime_in_seconds):
percentage_run_time = (
uptime_in_seconds/seconds_in_a_day) * 100
return 'The process was up {percent} percent of the
day'.format( percent=int(percentage_run_time))
uptime_in_seconds = 60 * 60 * 24
display_uptime(uptime_in_seconds)
🙆♀️
SECONDS_IN_A_DAY = 60 * 60 * 24
def display_uptime(uptime_in_seconds):
percentage_run_time = (
uptime_in_seconds/SECONDS_IN_A_DAY) * 100
return 'The process was up {percent} percent of the day'.format( percent=int(percentage_run_time))
# ...
uptime_in_seconds = 60 * 60 * 24
display_uptime(uptime_in_seconds)
2.PEP8 적용하기
pep8 - python 언어 표준 규칙
pep8을 적용하고 스타일 검사 플러그인을 설치하면 가독성이 좋아지고, 코드 관리에 좋음
3. 싱글라인 코드 지양하기
;을 사용해 싱글라인으로 코드를 짤 수 있지만 가독성이 좋지 않다.
🙅♀️
for element in my_list: print(element); print('--------')
🙆♀️
for element in my_list:
print(element)
print('--------')
Documentation
1. PEP-257에서 설명된 docstring 사용하기
pep257에서 형식적인 규칙을 제시한다. 좋은 문서를 작성하는 규칙은 pep-257을 따르면 도움이 된다.
🙅♀️
def calculate_statistics(value_list):
# calculates various statistics for a list of numbers
<The body of the function>
🙆♀️
def calculate_statistics(value_list):
"""Return a tuple containing the mean, median, and mode of a list of integers
Arguments:
value_list -- a list of integer values
"""
<The body of the function>
2. docstring 과 주석을 구분하자
초보자가 하는 실수는 과도하게 문서화 하는 것인데, 주석처리가 과도하다면 코드가 명확하지 않다는 것일 수 있다.
🙅♀️
def calculate_mean(numbers):
"""Return the mean of a list of numbers"""
# If the list is empty, we have no mean!
if not numbers: return 0
# A variable to keep track of the running sum
total = 0
# Iterate over each number in the list
for number in numbers: total += number
# Divide the sum of all the numbers by how
# many numbers were in the list
# to arrive at the sum. Return this value.
return total / len(numbers)
🙆♀️
def calculate_mean(numbers):
"""Return the mean of a list of numbers"""
return sum(numbers) / len(numbers)
3. 작동방식보다 무엇에 대한 코드인지 쓰기
코드가 수행하는 방식보다 코드가 수행하는 작업을 문서화해야 한다. 문서의 독자는 작동방식을 알 필요는 없다. 작동방식을 알 경우 추상화된 코드에 약점을 만들 수 있다.
🙅♀️
def is_prime(number):
"""Mod all numbers from 2 -> number and return True if the value is never 0"""
for candidate in range(2, number):
if number % candidate == 0:
print(candidate) print(number % candidate)
return False
return number > 0
🙆♀️
defs is_prime(number):
"""Return True if number is prime"""
for candidate in range(2, number):
if number % candidate == 0:
return False
3.imports
1. import문 순서대로 정리하기
import 수가 많아지는데, 아래 순서가 표준 순서.
- standard library modules
- third-party library modules installed in site-packages
- modules local to the current project
🙅♀️
import os.path
import concurrent.futures
from flask import render_template
from flask import (Flask, request, session, g,
redirect, url_for, abort,
render_template, flash, _app_ctx_stack)
import requests
if __name__ == '__main__':
import this_project.utilities.sentient_network as skynet
import this_project.widgets
import sys
🙆♀️
import concurrent.futures
import os.path
import sys
from flask import (Flask, request, session, g,
redirect, url_for, abort,
render_template, flash, _app_ctx_stack)
import requests
import this_project.utilities.sentient_network as skynet
import this_project.widgets
2. Absolute import 사용하기
절대 경로
sys.path 에서 도달할 수 있는 위치에서 모듈 위치 지정
<package>.<module>.<submodule>
상대경로
현재 파일시스템에서 모듈 위치를 기준으로 지정
..other_module import foo
상대경로는 네임 스페이스를 복잡하게한다. 큰 모듈의 경우 막대가 어디에서 왔는지 명확하지 않을 수 있다.
🙅♀️
from ...package import other_module
🙆♀️
import package.other_module
import package.other_module as other
3. * 사용하지말기
- 를 사용하지말고 가져오는 모듈 양이 많아지면,
()를 사용하자. 정의한 이름과 패키지에 이름이 충돌할 경우가 존재한다.
🙅♀️
from foo import *
🙆♀️
from foo import (bar, baz, qux, quux, quuux)
# or even better...
import foo
4. try block을 사용하여 패키지가 이용 가능한지 결정하기
라이브러리를 작성할 때 사용자의 컴퓨터에서 특정 패키지를 사용할 수 없다면, try/except 블록을 사용해서 대체 패키지를 가져와야한다.
🙅♀️
import cProfile
print(cProfile.__all__)
🙆♀️
try:
import cProfile as profiler
except:
import profile as profiler
print(profiler.__all__)
❓언제 이런게 있을지 궁금
5. 튜플을 사용하여 긴 리스트의 모듈 임포트하기
특정 모듈에서 많은 임포트가 발생할 때, 튜플을 사용하자.
🙅♀️
from django.db.models import AutoField, BigIntegerField, BooleanField, CharField
from django.db.models import CommaSeparatedIntegerField, DateField, DateTimeField
🙆♀️
from django.db.models import (AutoField, BigIntegerField, BooleanField, CharField, CommaSeparatedIntegerField, DateField, DateTimeField)
4. Modules and Packages
1. init.py 를 사용하여 패키지 인터페이스를 단순화하기
__init__.py 에 패키지를 임포트하여 단순화한다.
🙅♀️
# If the gizmo directory has an emtpy __init__.py,
# imports like the ones below are necessary, even
# if Gizmo and GizmoHelper are all that clients should ever need to use
from gizmo.client.interface import Gizmo
from gizmo.client.contrib.utils import GizmoHelper
🙆♀️
# __init__.py:
from gizmo.client.interface import Gizmo
from gizmo.client.contrib.utils import GizmoHelper
#client code:
from gizmo import Gizmo, GizmoHelper
5. 실행가능한 스크립트
1. if __name__ == '__main__' 패턴을 사용하여 파일을 가져와 실행하기
Python은 기본 진입점이 없고, 소스파일을 로드하는 즉시 명령문을 실행한다.
독립실행형 스크립트로 상용하기 위해서 if __name__ == '__main__' 를 사용하자.
🙅♀️
import sys
import os
FIRST_NUMBER = float(sys.argv[1])
SECOND_NUMBER = float(sys.argv[2])
def divide(a, b):
return a / b
# I can't import this file (for the super
# useful 'divide' function) without the following # code being executed.
if SECOND_NUMBER != 0:
print(divide(FIRST_NUMBER, SECOND_NUMBER))
🙆♀️
3.5.1.2 Idiomatic
import sys
import os
def divide(a, b):
return a / b
if __name__ == '__main__':
first_number = float(sys.argv[1])
second_number = float(sys.argv[2])
if second_number != 0:
print(divide(first_number, second_number))
2. python script를 직접 실행할수 있게 만들기
/usr/bin/env python 첫 번째 줄에서 스크립트를 직접 실행할 수 있다.
chmod +x <script_name> 명령을 통해 스크립트를 실행 가능하다.
“python manage.py ‘‘를 입력하는 대신 manage.py 파일을 직접 실행할 수 있기 때문입니다.
3. sys.exit를 사용하여 적절한 오류 코드를 반환
성공적으로 실행했을 때 0 을 반환하게 하려면 sys.exit 호출하면된다.
🙅♀️
if __name__ == '__main__':
import sys
if len(sys.argv) > 1:
argument = sys.argv[1]
result = do_stuff(argument)
if result:
do_stuff_with_result(result)
🙆♀️
def main():
import sys
if len(sys.argv) < 2:
sys.exit('You forgot to pass an argument')
argument = sys.argv[1]
result = do_stuff(argument)
if not result:
sys.exit(1)
do_stuff_with_result(result)
return 0
if __name__ == '__main__':
sys.exit(main())
5. sys.argv 사용해서 커맨드라인 파라미터 참조하기
sys.argv를 사용하여 들어온 파라미터 인자를 처리할 수 있다.
🙅♀️
import argparse
if __name__ == '__main__':
parser = argparse.ArgumentParser(usage="my_cat.py <filename>")
parser.add_argument('filename', help='The name of the file to use')
parsed = parser.parse_args(sys.argv)
print(open(parsed['filename']).read())
🙆♀️
if __name__ == '__main__':
try:
print(open(sys.argv[1]).read())
except IndexError:
print('You forgot the file name!')