ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Python] Effective Python 2 - Pythonic
    💫 Computer Science/Python & AI Framework 2021. 3. 1. 17:41

     

     

     

    Pythonic

     

    명시적이고, 단순하고, 가독성이 좋은것.

    Easy to read, Don't Repeat yourself

     

     

    How to make pythonic code

     

    1. 한줄로 굳이 작성해도 되지 않아도 될 것을 한줄로 작성해서 시각적 잡음을 일으키지 말자.

    from urllib.parse import parse_qs
    
    my_values = parse_qs("빨강=5&초록=0&파랑=", keep_blank_values=True)
    
    red = my_values.get("빨강", [""])[0] or 0
    print(f"빨강: {red!r}")
    green = my_values.get("초록", [""])[0] or 0
    print(f"초록: {green!r}")
    blue = my_values.get("파랑", [""])[0] or 0
    print(f"파랑: {blue!r}")
    
    # 값을 int로 바꿔야할 때
    red = int(my_values.get("빨강", [""])[0] or 0)
    # 복잡한 식을 한줄로 표현 X
    red_str = my_values.get("빨강", [""])[0] or 0
    red = int(red_str[0]) if red_str[0] else 0
    # 여러 줄로 나누어쓴 if else 문이 더 정확하다
    red_str = my_values.get("빨강", [""])[0] or 0
    if red_str[0]:
        red = int(red_str[0])
    else:
        red = 0

     

     

    2. or 이나 and 식을 사용하는 것 보단 if/else를 사용해라.

     

     

    3. 되도록 if/else 문을 여러줄로 작성해서 명확하게 만들어주어야 한다.

     

     

    4. 단지 두 세번 반복되는 로직일지라도 도우미 함수(help function)를 작성하자.

    # 도우미 함수를 쓰자 단지 2,3 번 반복에 불과하더라도
    def get_first_int(values, key, default=0):
        found = values.get(key, [""])
        if found[0]:
            return int(found[0])
        return default

     

     

    5. 빈 문자열, 빈 리스트, 0 모두 암시적으로 False가 된다는 것을 이용해라.

    • len대신 if not 을 사용
    • 딕셔너리 키가 없을 경우 KeyError 말고 get을 사용.
    from urllib.parse import parse_qs
    
    my_values = parse_qs("빨강=5&초록=0&파랑=", keep_blank_values=True)
    
    print(my_values)  # {'빨강': ['5'], '초록': ['0'], '파랑': ['']}
    
    # 값이 비어있거나 없는 경우 0으로 default 생성해주기
    
    # dic.get(key, default) key가 없는 경우 default로 저장
    # or -> False면 좌 값 아니면 우 값
    # 빈 문자열, 리스트, 0 모두 암시적으로 False 취급.
    red = my_values.get("빨강", [""])[0] or 0
    print(f"빨강: {red!r}")
    green = my_values.get("초록", [""])[0] or 0
    print(f"초록: {green!r}")
    blue = my_values.get("파랑", [""])[0] or 0
    print(f"파랑: {blue!r}")

     

     

    6. 절대로 for, while 뒤에 else를 사용하지 말 것.

    # 서로소 검사
    # for/while 뒤에 else 쓰지 X
    a, b = 4, 9
    for i in range(2, min(a, b) + 1):
        if a % i == 0 and b % i == 0:
            print("서로소 아님")
            break  # else 실행되지 않음
    else:
        print("서로소임")
    
    # 1. 도우미 함수를 작성해 바로 리턴
    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
    
    
    assert coprime(a, b)
    assert not coprime(3, 6)
    
    # 2. 도우미 함수를 작성해 변수 리턴
    def coprime_alternate(a, b):
        is_coprime = True
        for i in range(2, min(a, b) + 1):
            if a % i == 0 and b % i == 0:
                is_coprime = False
                break
        return is_coprime
    
    
    assert coprime_alternate(a, b)
    assert not coprime_alternate(3, 6)

     

     

    7. 대입식(:=)을 사용해 반복을 피하기 - walrus operator(assignment expression)

    python 3.8이상만 가능

    # 0인지 검사하는 패턴 파이썬에서 자주 사용
    def make_lemonade(count):
        pass
    
    
    def out_of_stock():
        pass
    
    
    fresh_fruit = {"사과": 10, "배": 9, "바나나": 6}
    count = fresh_fruit.get("레몬", 0)
    if count:
        make_lemonade(count)
    else:
        out_of_stock()
    
    # walrus operator = assignment expression 대입식
    # count가 if에만 영향을 준다는 것을 파악하기 쉽다.
    # 대입식은 대입문이 쓰일 수 없는 곳에도 쓸 수 있게 해줌.
    if count := fresh_fruit.get("레몬", 0):
        make_lemonade(count)
    else:
        out_of_stock()
    
    # 레몬은 0인지 아닌지만 검사하면 된다
    # 사과일 경우 4개 이상이 있는지 검사해야한다면
    if (count := fresh_fruit.get("사과", 0)) > 4:
        make_lemonade(count)
    else:
        out_of_stock
    
    
    ## 바나나 스무디 바나나2개 필요하고 없을 경우 OutOfBananas 발생
    def slice_banana(count):
        return count * 3
    
    
    class OutOfBananas(Exception):
        pass
    
    
    def make_smooth(count):
        return "smoothie"
    
    
    pieces = 0
    count = fresh_fruit.get("바나나", 0)
    if count >= 2:
        pieces = slice_banana(count)
    
    try:
        smoothies = make_smooth(pieces)
    except OutOfBananas:
        out_of_stock()
    
    # walrus
    pieces = 0
    if (count := fresh_fruit.get("바나나", 0)) >= 2:
        pieces = slice_banana(count)
    
    try:
        smoothies = make_smooth(pieces)
    except OutOfBananas:
        out_of_stock()

     

     

    8. do while과 switch 도 walrus를 사용해서 구현할 수 있다.

    # do while 없음
    import random
    
    
    def pick_fruit():
        if random.randint(1, 10) > 2:  # 80% 확률로 새 과일 보충
            return {
                "사과": random.randint(0, 10),
                "바나나": random.randint(0, 10),
                "레몬": random.randint(0, 10),
            }
        else:
            return None
    
    
    def make_juice(fruit, count):
        if fruit == "사과":
            return [("사과주스", count / 4)]
        elif fruit == "바나나":
            return [("바나나스무디", count / 2)]
        elif fruit == "레몬":
            return [("레모네이드", count / 1)]
        else:
            return []
    
    
    # 초기화하면서 pick_fruit() 호출, 마지막에 pick_fruit() 호출 2번 호출됨.
    bottles = []
    fresh_fruit = pick_fruit()
    while fresh_fruit:
        for fruit, count in fresh_fruit.items():
            batch = make_juice(fruit, count)
            bottles.extend(batch)
        fresh_fruit = pick_fruit()
    
    print(bottles)
    
    # 무한 루프는 제어가 모두 break에 달려있어버림
    bottles = []
    while True:  # 무한루프
        fresh_fruit = pick_fruit()
        if not fresh_fruit:  # 중간에서 끝내기
            break
    
        for fruit, count in fresh_fruit.items():
            batch = make_juice(fruit, count)
            bottles.extend(batch)
    
    print(bottles)
    
    # walrus로 매번 fresh_fruit에 대입하고 검사할 수 있다.
    bottles = []
    while fresh_fruit := pick_fruit():
        for fruit, count in fresh_fruit.items():
            batch = make_juice(fruit, count)
            bottles.extend(batch)
    
    print(bottles)

     

     

    # 가장 좋은 주스 먼저 제공하고, 바나나, 키위, 수박 제공
    fresh_fruit = {
        "사과": 10,
        "바나나": 8,
        "레몬": 5,
    }
    
    
    def make_lemonade(count):
        n = 1
        print(f"레몬 {count*n} 개로 레모네이드 {count//n} 개를 만듭니다.")
        fresh_fruit["레몬"] -= count * n
        return f'레몬이 {fresh_fruit["레몬"]} 개 남았습니다.'
    
    
    def out_of_stock():
        print(f"제료가 부족합니다. 재료를 보충해 주세요.")
    
    
    def make_cider(count):
        n = 4
    
        print(f"사과 {count} 개로 사과주스 {count//n} 개를 만듭니다.")
        fresh_fruit["사과"] -= n * (count // n)
        return f'사과가 {fresh_fruit["사과"]} 개 남았습니다.'
    
    
    def slice_bananas(count):
        print(f"바나나 {count} 개를 슬라이스합니다.")
        fresh_fruit["바나나"] -= count
        return count
    
    
    class OutOfBananas(Exception):
        pass
    
    
    def make_smoothies(count):
        n = 2
        if count > n:
            print(f"바나나 슬라이스 {count} 개로 스무디 {count//n} 개를 만듭니다.")
            return f'바나나가 {fresh_fruit["바나나"]} 개 남았습니다.'
        else:
            raise OutOfBananas
    
    
    ########### switch를 흉내내려면 if else elif를 깊게 내포시키는 것이다.
    # 좋지 않다.
    count = fresh_fruit.get("banana", 0)
    
    if count >= 2:
        pieces = slice_bananas(count)
        to_enjoy = make_smoothies(pieces)
    else:
        count = fresh_fruit.get("사과", 0)
        if count >= 4:
            to_enjoy = make_cider(count)
        else:
            count = fresh_fruit.get("레몬", 0)
            if count:
                to_enjoy = make_lemonade(count)
            else:
                to_enjoy = "아무것도 없음"
    
    # walrus를 이용하자
    if (count := fresh_fruit.get("바나나", 0)) >= 2:
        pieces = slice_bananas(count)
        to_enjoy = make_smoothies(pieces)
    elif (count := fresh_fruit.get("사과", 0)) >= 4:
        to_enjoy = make_cider(count)
    elif count := fresh_fruit.get("레몬", 0):
        to_enjoy = make_lemonade(count)
    else:
        to_enjoy = "아무것도 없음"
    

    댓글

Designed by Tistory.