본문 바로가기
C.W.K.
Stream
Lesson 07 of 07 · published

할당 표현식과 가드 절 — 흐름을 평평하게 만들기

~18 min · walrus, assignment-expression, short-circuit, or-default

Level 0호기심
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete

계산한 값을 바로 검사할 때

:=는 표현식 안에서 값에 이름을 붙여 같은 함수 호출이나 계산을 되풀이하지 않게 해. if·while·컴프리헨션에서 유용하지만, 새 이름이 문장을 짧고 분명하게 만들 때만 써.

or의 기본값은 거짓값도 덮는다

value or fallback은 value가 None일 때만이 아니라 0, 빈 문자열, 빈 리스트일 때도 대체값을 골라. 그런 값이 유효하다면 value is None을 명시해야 해.

가드 절로 실패를 먼저 돌려보내

함수 초반에 잘못된 입력과 특별한 경우를 짧게 반환하면 정상 흐름의 들여쓰기가 얕아져. 분기마다 상태를 늘리기보다 통과 조건을 차례로 좁히는 방식이야.

비어 있는 자리를 표시하는 세 방법

pass는 아무 동작도 하지 않고, ...는 아직 채우지 않은 자리를 눈에 띄게 하며, NotImplementedError는 호출됐을 때 구현되지 않았음을 명시적으로 실패시켜. 비교 메서드가 돌려주는 NotImplemented와는 다른 객체야.

Code

if와 while에서 계산한 값을 바로 검사하기·python
items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# walrus 없이
n = len(items)
if n > 5:
    print(f"big list ({n})")

# walrus 로 — 한 줄
if (n := len(items)) > 5:
    print(f"big list ({n})")

# while 안에서 — 청크 단위 읽기
import io
stream = io.BytesIO(b"hello world this is some data")
while chunk := stream.read(8):
    print(chunk)
# b'hello wo'
# b'rld this'
# b' is some'
# b' data'
컴프리헨션 안의 할당 표현식·python
values = [10, 20, 30, 40, 50]

# walrus 없이 — 비싼 부분 매번 재계산
result = [v*v for v in values if v*v > 500]

# walrus 로 — 한 번 계산, 결과에 이름
result = [sq for v in values if (sq := v*v) > 500]
print(result)              # [900, 1600, 2500]
or 기본값이 0과 빈 값도 덮는 함정·python
# or 디폴트는 0/빈값 = 없음 일 때 좋음
user_input = ""
name = user_input or "anonymous"
print(name)                # 'anonymous'

# 근데 0 이 유효 값이면 or 디폴트 잘못됨
def get_quantity(x):
    return x or 1          # 잘못됨: 0 도 1 로 변함

print(get_quantity(0))     # 1   <- 버그!
print(get_quantity(None))  # 1

# 맞음 — 명시적 None 체크
def get_quantity_v2(x):
    return x if x is not None else 1

print(get_quantity_v2(0))  # 0
print(get_quantity_v2(None))  # 1
가드 절로 정상 흐름 드러내기·python
# 중첩 if — 안 읽힘
def process_v1(user, msg):
    if user is not None:
        if user.is_active:
            if msg:
                if len(msg) <= 280:
                    return f"sending: {msg}"
                else:
                    return "too long"
            else:
                return "empty"
        else:
            return "inactive"
    else:
        return "no user"

# Guard clause — 평탄한 happy path
def process_v2(user, msg):
    if user is None:
        return "no user"
    if not user.is_active:
        return "inactive"
    if not msg:
        return "empty"
    if len(msg) > 280:
        return "too long"
    return f"sending: {msg}"
pass·...·NotImplementedError 구분하기·python
# 1. pass — 명시적 no-op
def nothing():
    pass

# 2. ... — Ellipsis 가 stub 으로. .pyi stub 파일과 추상 메서드에 흔함.
def stub():
    ...

# 3. NotImplementedError — 서브클래스가 *반드시* override
class Base:
    def required(self):
        raise NotImplementedError("subclass must override")

External links

Exercise

fetch_or_compute(cache, key, compute_fn)를 만들어 캐시에 값이 있으면 돌려주고, 없으면 compute_fn을 한 번 호출해 저장한 뒤 돌려줘. 할당 표현식을 한 번 이상 써. 이어서 None, 빈 문자열, @ 없음, @가 둘 이상, @ 뒤에 점 없음은 'invalid', 나머지는 'ok'validate_email을 가드 절로 작성해 각각 네 입력 이상 시험해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.