본문 바로가기

Python

[Clean Python] Python 코드 표현과 구문

 

영리한 코드를 작성하는 것은 가독성과 단순성에서 또 다른 문제를 야기할 수 있다.


users = [
    {"first_name": "Helen", "age": 39},
    {"first_name": "Buck", "age": 10},
    {"name": "anni", "age": 9}
    ]

users= sorted(user, key=lamda user: user["first_name"].lower())

 

위 코드는 한 라인에서 람다를 이용해 중첩 딕셔너리를 first_name으로 정렬하고 있으므로 루프를 사용하는 대신 딕셔너리를 정렬하는 영리한 방법으로 보이지만, 이 코드는 문제점이 있다.

 

물론 람다로 딕셔너리를 정렬할 수 있으므로 라인 수를 줄일 수 있었지만, 람다는 애시당초 쉬운 개념이 아니기에 새로운 개발자가 언뜻 봐도 코드를 이해하기 쉽지 않다. 람다는 이 코드를 명확하고, 읽기 쉽게 만들어주진 않는다는 의미이다. 더불어 키 누락이나 딕셔너리의 정확성 여부 같은 문제도 해결하지 못한다. -> 여러 라인을 한 라인으로 줄여주지만 복잡성이 주입된 케이스이다.

 

이번에는 위 코드를 리팩토링한 예시를 살펴보자


users = [
    {"first_name": "Helen", "age": 39},
    {"first_name": "Buck", "age": 10},
    {"name": "anni", "age": 9}
    ]


def get_user_name(users):
    """Get name of the user in lower case"""
    return users["first_name"].lower()

def get_sorted_dictionary(users):
    """Sort the nested dictionary"""
    if not isinstance(users, dict):
        raise ValueError("Not a correct dictionary")
    if not len(users):
        raise ValueError("Empty dictionary")

    users_by_name = sorted(users, key=get_user_name)
    return users_by_name

 

이 코드에서는 딕셔너리에서 예상하지 못한 모든 값을 확인하고, 이전의 한 라인의 코드보다 훨씬 읽기 쉽다.
한 라인의 코드가 나쁘다는 뜻이 아니라 우리가 생각해야할 요점은 한 라인 코드가 사용자 입장에서 읽기 어렵게 한다면 피하라라는 것이다.
이 부분은 우리가 코드를 작성하면서 의식적으로 결정해야하는 부분이다.

 

두 번째 예시로 CSV 파일을 읽고, 처리되는 라인 수를 확인하는 예제를 살펴보자.
읽을 수 있는 코드를 작성하는 것이 왜 중요한지, 그리고 코드를 읽을 수 있게 하는데 네이밍이 얼마나 큰 역할을 하는지를 알 수 있다.

코드를 helper 함수로 세분하면, production 코드의 특정 오류가 발생했을 때 복잡한 코드를 판독 가능하고 디버깅하기 쉽게 만드는데 도움이 된다.


import csv

with open("employee.csv", mode="r") as csv_file:
    csv_reader = csv.DictReader(csv_file)
    line_count = 0
    for row in csv_reader:
        if line_count == 0:
            print(f'Column names are {", ".join(row)}')
            line_count += 1
            print(f'\t{row["name"]} salary: {row["salary"]}'
                  f'and was born in {row["birthday month"]}.')
        line_count += 1
    print(f'Processed {line_count} lines.')

 

위 코드는 with 구문으로 몇 라인에 걸쳐 여러 가지 작업들을 수행하고 있지만, 이런 종류의 코드를 디버깅하는 것은 어렵다.
함수를 정의할 때 명확한 목표와 경계가 있는지 확인해야한다.

이제 아래 코드를 살펴보자.

import csv


def process_salary(csv_reader):
    """Process salary of user from csv file."""
    line_count = 0
    for row in csv_reader:
        if line_count == 0:
            print(f'Column names are {", ".join(row)}')
            line_count += 1
        print(f'\t{row["name"]} salary: {row["salary"]}')
        line_count += 1
    print(f'Completed {line_count} lines.')


with open('employee.txt', mode='r') as csv_file:
    csv_reader = csv.DictReader(csv_file)
    line_count = 0
    process_salary(csv_reader)

 

여기서는 with 구문에 모든 것을 작성하는 대신 헬퍼 함수를 생성했다. process_salary 함수가 어떤 역할을 하는지 명확하게 하는 것이다. 특정 예외처리를 하거나, 더 많은 데이터를 읽길 원하는 경우, 함수를 더 분리해 단일 책임 원칙 Single Responsibility Principle(SRP) 을 따를 수 있다.

 

 

tmi. va하다가 python 하니까 올해 재밌다 ㅎㅎㅎ