[python] if-elif-else / 리팩토링

Suhyeon Lee·2025년 1월 14일
0

자기주도학습

목록 보기
78/83
post-thumbnail

if-elif-else


elif 대신 if를 두 번 쓴 이유는?
elif를 안 쓰고 다 if로 써도 되나? 만약 그렇다면 elif는 왜 있는 걸까?

  • elif와 else
    → 사용 용도가 조금 다름
    • else
      • if 조건이 참이 아닌 다른 모든 경우를 의미
    • elif
      • 앞의 조건문이 아니고, 뒤에 오는 조건까지 참인 경우
      • 즉, else + if
    • 예시
if num > 10:
    print('num은 10 초과')
else:
    print('num은 10 이하')
if num2 > 10:
    print('num2는 10 초과')
elif: num2 < 10:
    print('num2는 10 미만')
else:
    print('num2는 10')

※ 질문 속 if문 중 첫 번째 if문과 두 번째 if문은 상황이 조금 다름

  • 첫 번째 if문:
    • 사용자의 입력이 q이면, while문을 break하는 것이므로 그 다음이 진행이 되지 않음
    • 반대로, 사용자의 입력이 q가 아니면, 그 다음이 계속 진행됨
    • 따라서, 이 경우에는 굳이 if문 다음에 else문을 쓸 필요가 없음

즉,

while~~~ 생략

    if guess == "q":
        break

    else:
        print("q가 입력되지 않았으므로 계속")

위 코드나 아래 코드나 같은 의미가 됨

while~~~ 생략

    if guess == "q":
        break

    print("q가 입력되지 않았으므로 계속")
  • 두 번째 if문
    • while문을 break하거나 하는 처리 없이, 단순히 정답과의 비교만 하고 있으므로 정답이면 해당 if문이 수행되고, 오답이면 다음의 else문이 수행됨
      • 이 경우 else문 없이 그냥 print문을 써 주게 되면 정답인 경우에도 "아쉽습니다…"라는 메시지가 나옴
    • 두 번째에서 elif가 아닌 또 if가 사용된 이유는 앞에 이미 쓰인 if문의 조건이 맞으면 break가 있으므로 while문이 종료되고, break 밑의 코드가 수행되지 않기 때문
      • break 문 아래가 수행되었다는 의미는 이미 첫번째 if문의 guess == "q"가 아니라는 의미이므로 더 이상 위 조건문에 대한 if는 필요하지 않음
      • 굳이 쓴다면 elif로 써줄 수도 있겠으나, 필요없는 elif문이어서 생략하는 것
    • 두번째 if문인 guess == english_world 는 첫번째 if문과는 아예 별도인 조건문이므로 새로 if를 써야 함
      • if의 나열은 이전 if 충족 여부 상관없이 계속 if가 효력이 있는 거고, elif는 앞의 if가 충족하지 않을 때만 효력이 있음
      • 2번째 if를 elif로 구현하는 게 논리적으론 더 맞지만 q 입력 시 break로 끝나버리니깐 elif를 쓰나 if를 쓰나 상관없음

추가: if문의 나열 vs if문 & elif문 사용 비교

  • if문의 나열만으로 사용하는 것은 elif문 사용보다 더 비효율적일 수 있음
if num > 10:
	print('num은 10 초과')

if num < 10:
	print('num은 10 미만')

if num == 10:
	print('num은 10')


if num2 > 10:
	print('num2는 10 초과')

elif: num2 < 10:
	print('num2는 10 미만')

else:
	print('num2는 10')

위의 예제에서 if문의 나열로 이루어진 num에 대한 예제는 num이 어떤 값이든지 상관 없이 세 개의 if문을 다 수행하게 됩니다.
하지만, num2에 대한 예제는 num2의 값에 따라서 세 개의 if문을 다 수행할 수도 있지만, 하나의 if문에서 끝날 수도 있죠.
실제로 num, num2가 20이라고 하면 num에 대한 if문은 세 개가 다 수행되지만 num2에 대한 if문은 제일 위에 있는 하나만 수행됩니다.
따라서, if, elif, else를 이해하고 이를 적절히 잘 사용하는 것이 중요하겠습니다.

중첩된 if-else 구문 리펙토링

출처

  • 조건문을 통해 로직을 처리하는 것은 필수적이지만, 조건이 복잡해지고 여러 조건이 중첩되면서 코드가 읽기 어렵고 유지보수가 힘들어지는 경우가 많음
  • 특히, 중첩된 if-else는 코드의 가독성을 떨어뜨리는 주요 원인 중 하나임 → 리팩토링하는 여러 가지 기법 알아보기!

Early Return 패턴

  • 함수의 실행 중 특정 조건을 만족하지 않으면 일찍 반환(return)하는 방식
    • 코드의 중첩을 줄여 가독성을 크게 향상시킬 수 있음
  • Early Return 패턴을 활용하면 불필요한 중첩을 방지할 수 있음
    • 코드의 중첩을 줄여 가독성을 크게 향상시킬 수 있음
# before
def process_data(data):
    if data is not None:
        if data.is_valid():
            if data.has_permission():
                return "Processing data"
            else:
                return "No permission"
        else:
            return "Invalid data"
    else:
        return "No data"
        
 # after
 def process_data(data):
    if data is None:
        return "No data" # Early Return
    if not data.is_valid():
        return "Invalid data"
    if not data.has_permission():
        return "No permission"
    
    return "Processing data"

before 코드의 흐름도를 표현하면 아래와 같은 것

→ 의도는 알겠지만 계속 조건을 중첩해서 들어가 생각해야 하기 때문에 코드 가독성이 좋지 않음

  1. if문 다음에 나오는 공통된 절차를 첫번째 분기점 내부에 각각 넣기
    • 코드 중복을 피하려고 꺼내놨더니 거꾸로로 다시 중복 시켜놓는 것으로 보일 수 있지만 이는 리팩토링에 있어 필요한 과정이니 그대로 이행할 것
    • 여기서 첫 번째 분기점이란, 코드 레벨의 첫 if문을 말함
  2. 분기점에서 짧은 절차부터 실행하도록 if문 반전시키기
  • not None을 거꾸로 None으로 구성해주면 코드를 뒤바꿀 수 있게 되어 짧은 부분이 위쪽으로 오게 되고 길다란 부분이 아래쪽으로 오게 됨
  1. 짧은 절차가 끝나면 return(함수 내부의 경우)이나 break(for문 내부의 경우) 로 일찍 중단점을 추가

중첩 조건문들을 하나하나 꺼내면서 리팩토링한 결과 코드의 모습은 중첩되지 않고 하나의 레벨에서 각 조건식을 판별하는 형태가 되게 된다. 이렇게 구성하면 로직이 의미하고자 하는 바를 한눈에 알 수 있고 조건식을 중첩하여 생각하지않고 분리하여 생각함으로써 코드를 빠르게 복기할 수 있게 된다.

Guard Clauses 패턴

  • 특정 조건을 검사해 빠르게 반환하거나 예외를 던지는 패턴
  • Early Return과 유사하지만, 주로 예외 처리나 특정한 조건을 걸러내는 데 자주 사용
# before
def calculate_discount(price, user):
    if price > 0:
        if user.is_premium():
            if user.has_discount_coupon():
                return price * 0.7
    return price


# after
def calculate_discount(price, user):
    if price <= 0:
        raise ValueError("Price must be greater than zero")
    
    if not user.is_premium():
        return price
    
    if not user.has_discount_coupon():
        return price
    
    return price * 0.7

조건문을 함수로 분리

  • 조건이 많은 경우 각 조건을 별도의 함수로 분리해 코드의 가독성과 재사용성을 높일 수 있음
# before
def validate_user(user):
    if user.is_active:
        if user.has_subscription:
            if user.is_email_verified:
                return True
    return False


# after
def is_active_user(user):
    return user.is_active

def has_valid_subscription(user):
    return user.has_subscription

def is_email_verified(user):
    return user.is_email_verified

def validate_user(user):
    if not is_active_user(user):
        return False
    if not has_valid_subscription(user):
        return False
    if not is_email_verified(user):
        return False
    return True

데이터 구조를 활용해 조건 제거

  • 복잡한 조건문을 데이터 구조를 사용해 간결하게 표현할 수도 있음
    • 특히 다중 조건을 처리할 때 유용
# before
def get_discount(user_type):
    if user_type == "premium":
        return 20
    elif user_type == "standard":
        return 10
    elif user_type == "basic":
        return 5
    else:
        return 0


# after
def get_discount(user_type):
    discounts = {
        "premium": 20,
        "standard": 10,
        "basic": 5,
    }
    return discounts.get(user_type, 0)

참고: 자바스크립트 조건문 리팩토링

profile
2 B R 0 2 B

0개의 댓글

관련 채용 정보