Better way 1

사용중인 파이썬 버전을 알아두라

python 2.x 버전은 2020년 1월 1일부터 더이상 지원되지 않는다.

# 버전확인 

import sys

print(sys.version_info)
sys.version_info(major=3, minor=7, micro=12, releaselevel='final', serial=0)

print(sys.version)
3.7.12 (default, Nov  4 2022, 00:01:41) 
[Clang 13.1.6 (clang-1316.0.21.2.5)]

Python 3.6+ 버전부터는 FastAPI 를 제공한다.
https://fastapi.tiangolo.com/ko/

Python 3.8 버전부터는 대입식을 제공한다.
https://docs.python.org/ko/3/whatsnew/3.8.html

Python 3.11 버전에서는 이전버전 3.10 에 비해 10~60% 속도향상에 힘쓰고 있다.
https://docs.python.org/3/whatsnew/3.11.html

Better way 2

PEP 8 스타일 가이드를 따르라

공백

  • 파일 안에서 각 함수와 클래스 사이에는 빈줄을 두 줄 넣어라
  • 클래스 안에서 매서드와 매서드 사이에는 빈줄을 한줄 넣어라
class A():
	def a_a():
    	print("a_a")
        
	def a_b():
    	print("a_b")


def a():
	print("a")


def b():
	print("b")
  • dictionary에서 키와 콜론(:) 사이에는 공백을 넣지 않고, 한 줄 안에 키와 값을 같이 넣는 경우에는 콜론 다음에 스페이스를 하나 넣는다.
sample_dict: {
	key1: value1,
    key2: value2,
    key3: value3,
}
  • 변수 대입에서 = 전후에는 스페이스를 하나씩만 넣는다
a = 1
  • 타입 표기를 덧붙이는 경우에는 변수 이름과 콜론 사이에 공백을 넣지 않도록 주의하고, 콜론과 타입 정보 사이에는 스페이스를 하나 넣어라
name: str = "Lucas"
age: int = 29

이외에 라인 길이는 79개 문자, 들여쓰기는 스페이스바 4번의 규칙이 있는데 저는 사실상 라인 150자 까지도 사용하기도 하고, 스페이스바 보다는 tab으로 들여쓰기를 주로 합니다.
해당 부분은 사람마다 다르게 편리한데로 사용하는 것 같습니다.

저는 black 라이브러리를 사용하여 편리하게 라인을 정리함.
black: https://pypi.org/project/black/

명명규약

  • 함수, 변수, 애트리뷰트는 snake_case
  • 클래스는 PascalCase
  • 상수는 모든글자를 대문자로 하되 단어사이를 밑줄로 연결(TAX, GODE_TAX, SILVER_TAX)

식과 문

  • 긍정식을 부정하지 말고, 부정을 내부에 넣어라
[GOOD]
if a is not b:
	~~~~

[BAD]
if not a is b:
	~~~~
  • 빈값을 검사할때 length 로 체크하지 말고 빈값을 False 로 취급하는 것을 활용해라
empty_value = ""
[GOOD]
if not empty_value:
	print("빈값")

[BAD]
if len(empty_value) == 0:
	print("빈값")
  • 반대의 경우 값이 있는지 확인 할 때도 length 로 체크하지 말고 True 로 취급하는 것을 활용해라
text_value = "Hi"
[GOOD]
if text_value:
	print(text_value)

[BAD]
if len(text_value) != 0:
	print(text_value)
  • 한 줄짜리 if 문이나 한줄짜리 for, while 루프, 한 줄따라 except 복합문을 사용하지 말라
    코드 가독성을 위해서 인듯하지만 저는 정말 단순한 if, for 문은 한줄에 사용하기도 한다
value = 3
answer = "짝수" if value % 2 == 0 else "홀수"
print(answer)

num_list = [1, 2, 3, 4, 5]
new_num_list = [num * 2 for num in num_list]
print(new_num_list)
  • 식을 한 줄안에 다 쓸수 없는 경우, 식을 괄호로 둘러싸고 줄바꿈과 등여쓰기를 추가해서 가독성을 높힌다
  • 여러 줄에 걸쳐 식을 쓸때는 줄이 계속된다는 \ 문자보다는 괄호를 사용해라
num, num_value, arr_list, dict_value, tuple_value = 0, "", [], {}, ()

if (
    num // 2 is 0
    and not num_value
    and not arr_list
    and not dict_value
    and not tuple_value
):
    print("")
else:
    print("")

임포트

  • import 문(from x import y 도 포함)을 항상 파일 맨 앞에 위치한다
  • 모듈은 임포트할 때는 from bar import foo 이런식으로 사용한다 import foo 금지
  • 만약 반드시 상대경로로 import 해야 한다면 from . import foo 처럼 명시적인 구문으로 사용

저는 isort 라이브러리를 사용하여 편리하게 import 구문을 정리함.
isort: https://pypi.org/project/isort/

PEP8 원문: https://peps.python.org/pep-0008/
PEP8 번역: https://wikidocs.net/7896

Better way 3

Bytes 와 str 의 차이를 알아두라

bytes 와 str 은 같은 문자열 처럼 보이지만 bytes 는 앞에 b"문자열" 이런식으로 앞에 'b' 를 붙혀서 bytes 로 표시한다

bytes 로 된 문자열을 처리할때는 아래와 같이 decode("utf-8") 를 이용해서 str 형태로 만들거나,
파일 읽기/쓰기 할때는 'w', 'r' 옵션에서 'b' 를 추가해서 'wb', 'rb' 옵션을 준다

bytes -> str 로 변환

def to_str(bytes_or_str):
    if isinstance(bytes_or_str, bytes):
        return bytes_or_str.decode("utf-8")
    
    return bytes_or_str


print(to_str(b"foo"))
print(to_str("bar"))
print(to_str(b"\xed\x95\x9c"))

str -> bytes 로 변환

def to_bytes(bytes_or_str):
    if isinstance(bytes_or_str, str):
        return bytes_or_str.encode("utf-8")
    
    return bytes_or_str

print(to_bytes(b"foo"))
print(to_bytes("bar"))
print(to_bytes("한글"))

바이트 파일 쓰기

with open("data.bin", "wb") as f:
    f.write(b'\xed\x95\x9c\xea\xb8\x80')

바이트 파일 읽기

with open("data.bin", "rb") as f:
    print(f.read())

현재 Python encoding 확인

  • 파이썬 3.x 부터는 기본인코딩이 utf-8 이다
import locale
print(locale.getpreferredencoding())

Better way 4

C 스타일 형식 문자영을 str.format과 쓰기보다는 f-문자열을 통한 인터폴레이션을 사용하라

Python 에서도 C 처럼 출력할 떄 format 을 이용해서 숫자, 문자 등을 표시할 수 있다
하지만 C 스타일은 %d, %i, %s 등 사용시 휴먼에러 발생할 가능성도 크고 가독성이 떨어진다

그래서 저는 무조건적으로 f-string 을 사용한다


text = "글자"

num = 123

float_num = 123.4567

places = 3

print("format 활용 text: {0}, num: {1}, float_num: {2}, places_float_num: {3:.{4}f}".format(text, num, float_num, float_num, places))
print("format 활용 text: {}, num: {}, float_num: {}, places_float_num: {:.{}f}".format(text, num, float_num, float_num, places))
print(f"f-string 활용 text: {text}, num: {num}, float_num: {float_num}, places_float_num{float_num:.{places}f}")

Better way 5

복잡한 식을 쓰는 대신 도우미 함수를 작성하라

from urllib.parse import parse_qs

my_values = parse_qs("빨강=5&파랑=0&초록=", keep_blank_values=True)
print(my_values)

# .get 함수를 사용할 때 값이 없으면 자동으로 None 값으로 출력
print(f'빨강: {my_values.get("빨강")}')
print(f'초록: {my_values.get("초록")}')
print(f'투명도: {my_values.get("투명도")}')

# .get 함수 사용 후 None, 0 인 경우 False 로 취급돼서 or 연산자에 의해서 0 으로 출력
red = my_values.get("빨강", [""])[0] or 0
green = my_values.get("초록", [""])[0] or 0
opacity = my_values.get("투명도", [""])[0] or 0
print(f'빨강: {red}')
print(f'초록: {green}')
print(f'투명도: {opacity}')

# 식을 간결하게 쓰기
red_str = my_values.get("빨강", [""])
red = int(red_str[0]) if red_str[0] else 0
print(f'red: {red}')

# 식을 명확하게 쓰기
green_str = my_values.get("초록", [""])
if green_str[0]:
    green = int(green_str[0])
else:
    green = 0
print(f'green: {green}')

# 도우미 함수를 이용하여 간결하고 명확하게 쓰기
def get_first_int(values, key, defalut=0):
    found = values.get(key, [""])
    if found[0]:
        return int(found[0])
    return defalut

print(f'빨강: {get_first_int(my_values, "빨강")}')
print(f'초록: {get_first_int(my_values, "초록")}')
print(f'투명도: {get_first_int(my_values, "투명도")}')

Better way 6

인덱스를 사용하는 대신 대입을 사용해 데이터를 언패킹하라

favorite_snacks = {
    "짭조름한 과자": ("프레즐", 100),
    "달콤한 과자": ("쿠키", 180),
    "채소": ("당근", 20),
}

# 언패킹 하여 변수에 바로 대입
print(f"favorite_snacks.items() => {favorite_snacks.items()}")
(
    ((type1), (name1, cals1)),
    ((type2), (name2, cals2)),
    ((type3), (name3, cals3)),
) = favorite_snacks.items()

print(f"제일 좋아하는 {type1} 는 {name1}, {cals1} 칼로리입니다.")
print(f"제일 좋아하는 {type2} 는 {name2}, {cals2} 칼로리입니다.")
print(f"제일 좋아하는 {type3} 는 {name3}, {cals3} 칼로리입니다.")


# 버블정렬
def bubble_sort(names):
    for _ in range(len(names)):
        print("-----")
        for i in range(1, len(names)):
            if names[i] < names[i - 1]:
                temp = names[i]
                names[i] = names[i - 1]
                names[i - 1] = temp

            print(names)


names = ["프레즐", "당근", "쑥갓", "베이컨"]
print("\n버블정렬")
bubble_sort(names)
print(names)


# 언패킹을 사용한 버블정렬
def bubble_sort(names):
    for _ in range(len(names)):
        print("-----")
        for i in range(1, len(names)):
            if names[i] < names[i - 1]:
                names[i - 1], names[i] = names[i], names[i - 1]

            print(names)


names = ["프레즐", "당근", "쑥갓", "베이컨"]
print("\n언패킹을 사용한 버블정렬")
bubble_sort(names)
print(names)


# enumrate 와 언패킹 활용
snaks = [("베이컨", 350), ("도넛", 240), ("머핀", 190)]
for idx, (name, cal) in enumerate(snaks, 1):
    print(f"#{idx}: {name}은 {cal} 칼로리 입니다.")

Better way 7

range보다는 enumerate를 사용하라

개인적으로 for 문을 사용할 때는 range 사용을 최소화하는게 좋다

# 파이썬 반복문 사용
flavor_list = ["바닐라", "딸기", "초코"]
for flavor in flavor_list:
    print(f'{flavor}맛')

# range 사용
for i in range(len(flavor_list)):
    print(f'{i + 1}: {flavor_list[i]}')
    
# enumrate 사용
flavor = enumerate(flavor_list)
print(next(flavor))
print(next(flavor))
print(next(flavor))
# print(next(flavor)) # 4 번째 값은 없어서 StopIteration 에러 출력

# enumerate 두번째 인자값으로 수를 세기 시작하는 값 지정
for idx, flavor in enumerate(flavor_list, 1):
    print(f"{idx}: {flavor}")

Better way 8

여러 이터레이터에 나란히 루프를 수행하려면 zip을 사용하라

zip 을 사용해서 이터레이터를 합칠수 있다
합친 이터레이터 값의 개수가 다른경우 itertools.zip_longest 를 활용하여 빈값을 채워준다

import itertools

names = ["Lucas", "이진수"]
name_len = [len(name) for name in names]

names.append("abcdefg")

# itertools.zip_longest 에서 fillvalue 파라미터를 이용해서 빈값을 특정 값으로 치환해줄수 있다 defalut 는 None 이다.
for name, name_len in itertools.zip_longest(names, name_len, fillvalue="null"):
    print(f'{name}: {name_len}')

Better way 9

for나 while 루프 뒤에 else 블록을 사용하지 말라

파이썬을 공부하면서 아래와 같이 작성하는 구절은 본적이 없다
그만큼 while 문이나 for 문 뒤에 else 는 절대적으로 사용하지 않는다.

while False:
    print("실행되지 않는 줄")
else:
    print("While else block")

a = 4
b = 9
for i in range(2, min(a, b) + 1):
    print(f"검사중: {i}")
    if a % i == 0 and b % i == 0:
        print("서로수 아님")
        break
else:
    print("서로수")

# 도우미 함수를 활용하자
def coprime(a, b):
    for i in range(2, min(a, b) + 1):
        if a % i == 0 and b % i == 0:
            return False

    return True


print(coprime(4, 9))

Better way 10

대입식을 사용해 반복을 피하라

대입식을 사용하면 코드의 가독성이 좋아지고 줄수고 짧아진다
그러나 아직 익숙하지 않다

fresh_fruit = {
    "사과": 10,
    "바나나": 8,
    "레몬": 5,
}

count = fresh_fruit.get("레몬", 0)
if count >= 4:
    print("make lemonade")
else:
    print("no fruit")
    

# 대입식 사용
# vscode python 3.8 이하 버전에서 사용 시 에러 발생
# Operator ":=" requires Python 3.8 or newer
#   File "/Users/dlwlstn/soomgo_git/test/t1.py", line 16
#     if count := fresh_fruit.get("레몬", 0):
#               ^
# SyntaxError: invalid syntax

# 파이썬 3.8 부터 사용가능
if (count := fresh_fruit.get("레몬", 0)) >= 4:
    print("make lemonade")
else:
    print("no fruit")
profile
Python, Django, Flask, MySQL, AWS, Algorythm

1개의 댓글

comment-user-thumbnail
2023년 1월 28일

요악 및 느낌점

파이썬 2.x 버전은 더이상 사용하지 않고, 3.x 버전을 사용한다
이것 또한 개인적으로 실무하면서 느낀점은 최소 3.7 이상 버전을 사용하는 것이 좋을 것 같다

black, isort 라이브러리를 사용하여 코드를 관리하면 편리하다

또한 무조건 문자열 포멧팅을 f-string 을 사용하고, enumrate, zip 등 내장함수를 활용하면 코드 퀄리티가 높아진다.

3.8 이후부터 나온 대입식은 아직 생소하기도 하지만 유용하게 사용가능하다

파이썬은 쉽고 빠르게 배울 수 있고, 내장함수가 기능의 퀄리티가 좋고 많은 라이브러리가 있다

답글 달기