회사에서 요즘 코드 정리하는 태스크에 집중하고 있는데 작성되어 있는 Python코드가 카오스 그 자체라 다시한 번 깔끔한 코드를 작성하는 것이 얼마나 중요한 것인가를 깨닫고 무엇을 하면 안되는지 메모했다.
참고하면 좋은 사이트
Python에는 문법만큼 지켜야 하는 작성규칙이 있다
개행
- import는 가능한 한 최상단에서만 이루어져야 한다
- import아래에 바로 함수 혹은 클래스가 나온다면 2줄 개행하고 변수가 나온다면 1줄 개행한다
import os
import sys
def do_sth():
raise NotImplementError
- 클래스 내부는 1줄 개행한다
class Example:
def __init__(self):
raise NotImplementError
def do_sth(self):
raise NotImplementError
- 클래스 외부에서는 2줄 개행한다
def do_sth1():
raise NotImplementError
def do_sth2():
raise NotImplementError
- 동일 스크립트 내에서 작성한 클래스 혹은 함수를 실행하기 위해 하단부에 적는 실행 코드는 main 내부에 있어야 한다
- main위로 2줄 개행한다
if __name__ == '__main__':
...
-
스크립트 마지막에 1줄 개행한다
- 한 줄은 너무 길면 안되고 중간에 개행해야 한다(정식으로는 79자로 제한하고 있다)
-
연산자로 이어진 긴 코드라면 연산자 직전에 개행해야 한다
income = (gross_wages
+ taxable_interest
+ (dividends - qualified_dividends)
- ira_deduction
- student_loan_interest)
-
if문이나 for문의 처리는 무조건 개행해서 적어야 한다
- 원라이너로 적고 싶다고 한 줄에 다 적어버리면 안된다
if arg == "arg":
return True
들여쓰기
- 코드 옆에 코멘트를 작성할 때에는 2번 띄고 #를 적고 1번 띈 다음에 내용을 적는다
- 새 라인에 코멘트를 적는다면 #뒤에 1번만 띄어쓰면 되지만 indent를 맞춰야 한다
# do_sonthing function
def do_sth():
# pass
pass # do_something
- Python은 indent가 중요한 언어이기 때문에 스페이스든 탭이든 통일해서 제대로 맞춰준다
- 하나의 indent는 스페이스 4번이다
import
- 같은 라이브러리 혹은 모듈에서 import하는 것이 아닌 이상 개행한다
import os
import sys
from pathlib import Path
from datetime import timedelta, timezone, datetime
import tqdm
from tqdm.notebook import tqdm
- 작업하고 있는 디렉토리를 모듈로서 import해 사용하고 싶다면
__init__.py
를 만들어 import가능한 클래스 혹은 함수를 import해 놓는다- 같은 디렉토리의 다른 스크립트를 import하고 싶은 거라면 앞에
.
을 찍어야 한다 - 다른 디렉토리의 스크립트를 import하고 싶은 거라면 그 경로를 지정해줘야 한다
- 같은 디렉토리의 다른 스크립트를 import하고 싶은 거라면 앞에
# directory_name: my_module
# file_name: my_file
# function_name: my_function
# __init__.py
from .my_file import my_function
# directory_name: another_module
# function_name: another_function
# in another_module/another_file
from my_module.my_file import my_function
이름
- 이름을 정하지 않는 argument는 보통 arg라 적는다
- 마찬가지로 keyargument는 kwarg라 적는다
def my_func(arg, kwarg=None):
print(arg)
print(kwarg)
def my_func(some_arg, *args):
print(some_arg)
for arg in args:
print(arg)
- 클래스 이름이 아닌 이상 전부 소문자 snake case로 명명한다
- 클래스 이름은 대문자로 시작하는 camel case로 명명한다
class MyClass:
def __init__(self, arg):
self.arg = arg
def print_arg():
print(self.arg)
- 메서드 이름은 절대 변수 이름으로 사용해선 안된다
obj = object(some_arg)
dictionary = dict(some_arg)
some_list = list(some_arg)
- 클래스 내부 메서드가 private이라면 이름 앞에
_
을 붙인다 - Python의 빌트인기능을 사용해 그 값을 반환하는 메서드라면
__
로 감싸서 호출한다
class CrazyNumber(object):
def __init__(self, n):
self.n = n
def __add__(self, other):
return self.n - other
def __sub__(self, other):
return self.n + other
def __str__(self):
return str(self.n)
띄어쓰기
- kwarg에 값을 적을 때에는
=
좌우로 띄어쓰면 안된다- 일반 파라미터는
=
좌우로 띄어써야 한다
- 일반 파라미터는
def some_kwarg(kwarg=True):
if kwarg:
print("True")
else:
print("False")
if __name__ == '__main__':
kwarg = False
some_kwarg(kwarg=kwarg)
- 하지만 힌트를 줄 때에는 반대이다
def some_kwarg(kwarg: bool = True):
if kwarg:
print("True")
else:
print("False")
if __name__ == '__main__':
kwarg = False
some_kwarg(kwarg=kwarg)
- 예쁘게 쓴답시고
=
의 열을 맞추면 안되고 좌우로 무조건 1번씩만 띄어쓰기 해야 한다
arg = "arg"
some_arg = "something"
some_key_arg = "something secret"
데이터 형태
- str, int, float 등은 소문자로 힌트를 줘도 되지만 list, dict, tuple 등
typing
에서 import할 수 있는 것들은 import해서 힌트를 줘야 한다
from typing import List, Dict, Tuple
def return_list(some_list: List[str]) -> List:
return some_list
def return_dict(some_dict: Dict[str: str]) -> Dict:
return some_dict
def return_tuple(some_tuple: Tuple[str, str]) -> Tuple:
return some_tuple
bool
값을 판정할 때에는==
이나!=
을 사용하면 안된다is
혹은is not
을 사용해야 한다
def check_arg(arg):
if arg is not False:
return arg
elif arg is False:
return "wrong"
elif arg == "string":
return "wrong"
else:
raise ValueError("what is it?")
except
뒤에는 반드시 에러를 지정해야 한다
try:
import platform_specific_module
except ImportError:
platform_specific_module = None
전체 내용이 한단락이면 읽기 싫어진다
-
최대한 기능별로 나누어 직교성과 독립성을 갖춘다
- 꼭 필요한 상황이 아니라면 모듈로 만들어놓고 import해서 사용하는 것이 좋다
-
메소드 내부의 코드가 하나의 기능을 구현하기 위한 하나의 이야기라지만 상황이 바뀔 때마다 개행한다
from typing import List
def do_sth(arg: List[str]):
arg = arg.split('/') # split
for a in arg: # print
print(a)
if __name__ == '__main__':
arg = "/project/service/key/path"
-
개인적으로는 2줄 이하로는 개행하지 않는 것이 읽기 편하지만 그 이상이면 수행하는 작업이 진전되는 순간에 개행하는 것이 이해하기 쉽다
-
import할 때 builtin모듈을 위에 적고 1줄 개행하여 라이브러리 혹은 직접 구현한 모듈을 import하면 알기 쉽다
- 짧은 것부터 적는 것이 코드가 깔끔하다
import re
import sys
from pathlib import Path
- if문을 사용할 때 elif를 사용하지 않는다면 else가 없어도 상관 없지만 elif를 사용할 때에는 무조건 else를 지정해주는 것이 에러를 막는데 도움이 된다
- python3.x이라면 format-string보단 f-string을 사용하는 것이 좋다
def return_sentence(name, age):
return f"Hello, My name is {name}. I am {age} olds."
- python3.x라면
os.path.join()
보단pathlib.Path
로 묶어주는 것이 좋다
from pathlib import Path
directory = Path(__file__).resolve().parent
file_path = directory / "file_name"
- 지금 귀찮더라도 나중을 위해 코드를 완벽하게 구현할 수 있도록 노력해야 한다
- 반복작업은 없어야 한다
- e.g. 같은 모듈에서
import
되는 경우가 반복된다면from import
를 사용한다
from datetime import timedelta, timezone, datetime
this_day_in_last_week = (datetime.now(timezone.utc) - timedelta(days=-7)).strftime('%Y-%m-%d')
- 클래스의 메서드가
self
를 호출하지 않는다면@classmethod
로 래핑한다
class PathSplitter:
def __init__(self, arg: str):
self.arg
@classmethod
def split_text(cls, text: str, point_str: str):
return text.split(point_str)
def return_split_text(self):
return split_text(self.arg, "/")
if __name__ == '__main__':
arg = "/project/service/key/path"
my_class = MyClass(arg)
print(my_class().return_split_text())
- 리스트 내포문을 적극적으로 사용한다
if else
문을 없앨 수 있다- 하지만 반환값이 없는데 원라이너로 쓰고 싶다고 무의미하게 리스트를 생성해선 안된다
def normal_list_comprehension(*args):
return [arg for arg in args]
def list_comprehension_with_if(*args):
return [idx for idx, arg in enumerous(args) if arg is True]
def list_comprehension_with_if_else(*args):
return [idx if arg is True else arg for idx, arg in args]
'파이썬3 노트' 카테고리의 다른 글
pipenv로 libmf나 implicit을 install할 때 cmath관련 에러가 난다면 (0) | 2021.01.12 |
---|---|
Pycharm을 anaconda환경으로 설정하기 (0) | 2020.05.28 |
외부 서비스의 데이터를 python으로 slack에 통지하기 (0) | 2020.04.14 |
ElementTree로 xml파일 읽어오기 (2) (0) | 2019.07.08 |
allennlp의 elmo.md 알고 싶은 부분만 적당히 직역 (0) | 2019.05.12 |