Skip to content
This page has been auto-translated and may contain errors.View in English

함수

docs.scrimba.com

프로그램이 커질수록 여러 곳에서 같은 로직을 작성하게 됩니다. 함수는 로직을 한 번 작성하고, 이름을 붙인 후 어디서든 사용하게 해줍니다. 한 곳에서 수정하면 모든 호출이 자동으로 수정됩니다.

함수는 코드 재사용과 추상화의 주요 단위입니다. 동작을 감싸고, 이름을 붙이고, 명확한 인터페이스(매개변수와 반환값)를 정의하고, 어디서든 호출할 수 있게 합니다. 잘 지은 함수명은 문서처럼 읽힙니다: validate_email()은 몸체를 읽지 않아도 그 코드 블록이 무엇을 하는지 알려줍니다.

Python에서 함수는 첫 번째 클래스 객체입니다: 다른 것처럼 전달할 수 있는 값이므로, 변수에 할당하고, 리스트나 딕셔너리에 저장하고, 다른 함수에 인수로 전달하고, 함수에서 반환할 수 있습니다. def는 그 객체를 생성하고 현재 스코프(그 이름이 보이는 코드 영역)에 바인드합니다. 함수를 값으로 다루는 것이 sorted(key=...)와 작은 플러그인 테이블 같은 패턴을 가능하게 하며, 초반부터 편해질 가치가 있습니다.

python
def greet(name):
    return f"Hello, {name}!"

print(greet("민지"))   # "Hello, 민지!"
print(greet("준호"))     # "Hello, 준호!"

한 번 작성하고, 어디서든 사용하고, 한 곳에서 수정하세요.

함수 정의하기

def 키워드는 함수 정의를 시작하고, 이름, 괄호, 콜론, 그리고 들여쓰기된 몸체가 따릅니다. 함수는 호출할 때까지 아무것도 하지 않습니다. def로 정의한 후, 이름과 ()로 호출하세요.

def는 함수 객체를 생성하고 현재 스코프의 주어진 이름에 바인드하는 문입니다. 몸체는 정의 시점에 실행되지 않으며, 함수가 호출될 때만 실행됩니다. return 문이 없는 함수는 암묵적으로 None을 반환합니다.

def는 함수 객체를 만들고 현재 스코프의 이름에 바인드합니다. 몸체는 Python이 def를 읽을 때 실행되지 않으며, 함수를 호출할 때만 실행됩니다. 그 분리가 실제로 중요합니다: 함수 몸체 내의 오타(존재하지 않는 이름 같은)는 함수가 실제로 호출될 때까지 조용히 있으므로, 절대 호출하지 않는 함수는 NameError를 숨길 수 있으며 그것이 프로덕션에서 누군가 호출하는 그날에만 드러납니다.

python
def say_hello():
    print("Hello!")

say_hello()   # 함수 호출
Juno함수 정의하기def name():를 작성하고 몸체를 들여쓰세요. name()으로 호출할 때까지 아무것도 일어나지 않으며, def는 그저 설정할 뿐입니다. 그리고 return을 잊으면 함수는 조용히 None을 돌려주는데, 초반에 이게 나를 더 한 번 낚았습니다.
Juno함수 정의하기def는 함수를 생성하고 이름에 바인드하지만, 몸체는 호출할 때만 실행됩니다. return이 없으면 함수는 None을 반환합니다. 결과가 뚜렷한 이유 없이 비어 오는 경우를 유념하세요.
Juno함수 정의하기 몸체는 def 시간에 실행되지 않으며, 함수를 호출할 때만 실행되므로, 내부의 깨진 이름은 실제로 무언가가 호출할 때까지 무시될 수 있습니다. return이 없는 함수는 None을 반환하는데, 이는 호출자가 값을 기대할 때의 그 자체의 조용한 버그입니다.

매개변수와 인수

매개변수는 함수가 기대하는 입력값입니다. 괄호 안에 나열하세요. 함수를 호출할 때, 전달하는 값들이 순서대로 매개변수와 일치합니다.

매개변수는 함수의 인터페이스를 정의합니다. 인수는 호출 시점에 전달되는 구체적인 값입니다. 위치 인수는 위치로 일치하고, 키워드 인수는 이름으로 일치합니다. 기본값은 매개변수를 선택적으로 만듭니다.

호출 시점에 위치 인수는 왼쪽에서 오른쪽으로 바인드되고 키워드 인수는 이름으로 바인드됩니다. 같은 매개변수를 두 가지 방식으로 전달하거나 Python이 배치할 수 없는 것을 전달하면 TypeError를 호출 시점에 일으키며, 몸체 내부에서는 아닙니다. Python은 또한 시그니처를 제한하게 해줍니다: 맨 *는 그 뒤의 모든 것을 키워드 전용으로 만들고(호출자는 이름을 붙여야 함), /는 그 앞의 모든 것을 위치 전용으로 만듭니다. 불린 플래그와 넓은 시그니처에서 키워드 전용을 사용하세요. 잘못된 자리에 위치 True는 잘 컴파일되고 버그를 배포하는 종류의 실수입니다.

python
def greet(name, greeting):
    print(f"{greeting}, {name}!")

greet("민지", "Hello")    # "Hello, 민지!"
greet("준호", "Hi")         # "Hi, 준호!"

매개변수는 함수 정의의 이름입니다. 인수는 호출할 때 실제로 전달하는 값입니다. 실제로는 사람들이 단어를 바꿔 사용합니다. 구별은 주로 문서를 읽을 때 중요합니다.

Juno매개변수와 인수 매개변수는 괄호에 나열하는 이름이고, 인수는 호출할 때 실제로 건네는 값입니다. Python은 왼쪽에서 오른쪽으로 정렬하므로, 전달하는 순서가 도착하는 순서입니다.
Juno매개변수와 인수 매개변수는 정의의 이름이고, 인수는 호출의 값입니다. 위치 인수는 왼쪽에서 오른쪽으로 바인드되므로, 위치가 계약입니다. 두 단어는 바꿔 사용되며, 간격은 문서를 읽을 때만 중요합니다.
Juno매개변수와 인수 위치 인수는 왼쪽에서 오른쪽으로 바인드되고, 키워드 인수는 이름으로 바인드되며, 충돌은 호출 시점에 TypeError를 일으킵니다. 맨 *는 호출자가 그 뒤의 것을 이름 붙이도록 강제하는데, 이는 호출 지점에서 True/False 플래그가 추측 게임이 될 함수의 저렴한 수정입니다.

기본값

매개변수에 기본값을 줄 수 있습니다. 호출자가 그 인수를 제공하지 않으면 기본값이 사용됩니다. 기본값이 있는 매개변수는 기본값이 없는 매개변수 뒤에 와야 합니다.

기본값은 매개변수를 선택적으로 만듭니다. 이들은 정의 시점에 한 번 평가되며, 각 호출마다가 아닙니다. 이것은 가변 기본값에 중요합니다: def f(items=[])은 모든 호출에서 같은 리스트를 공유합니다. 수정은 None을 기본값으로 사용하고 함수 몸체 내에서 리스트를 생성하는 것입니다.

자세한 내용은 물어뜯는 것입니다: 기본값은 def가 실행될 때 한 번 빌드되고 그것을 사용하는 모든 호출에서 공유됩니다. 0 또는 "Hello"처럼 불변인 기본값(제자리에서 변경할 수 없음)의 경우 괜찮습니다. 리스트나 딕셔너리처럼 가변인 기본값(제자리에서 변경할 수 있음)의 경우 함정입니다. 기본값으로 폴백하는 모든 호출이 같은 객체를 재사용하기 때문입니다:

python
def add_item(item, items=[]):   # 리스트는 한 번 생성되고 영원히 공유됨
    items.append(item)
    return items

add_item("a")   # ['a']
add_item("b")   # ['a', 'b']  <- 새 리스트가 아님

수정은 표준적인 것입니다: None으로 기본값 설정한 후 몸체 내에서 컨테이너를 빌드합니다.

python
def add_item(item, items=None):
    if items is None:
        items = []
    items.append(item)
    return items
python
def greet(name, greeting="Hello"):
    print(f"{greeting}, {name}!")

greet("민지")           # "Hello, 민지!"
greet("민지", "Hi")     # "Hi, 민지!"

기본값이 있는 매개변수는 기본값이 없는 매개변수 뒤에 와야 합니다.

Juno기본값 매개변수에 기본값을 주면 선택적이 되고, 호출자는 그것을 건너뛸 수 있습니다. 기본값이 있는 것들은 필수 것들 뒤에 와야 하며, 그 순서는 Python이 시행하는 규칙입니다. 모든 호출에서 같은 값을 작성하는 것을 줄여줍니다.
Juno기본값 기본값은 매개변수를 선택적으로 만들고 필수 것들 뒤에 앉아야 합니다. 함정은 기본값이 정의 시점에 한 번 평가되므로 절대 `[]` 같은 가변 기본값을 사용하지 마세요. `None`으로 기본값 설정하고 몸체 내에서 리스트를 빌드하세요.
Juno기본값 기본값은 `def` 시간에 빌드되고 호출 간에 공유되므로, `items=[]`은 조용히 호출 간에 누적됩니다. `None`으로 기본값 설정하고 몸체에서 새로운 것을 만드세요. 이것은 거의 다른 Python 슬립보다 더 실제 코드 리뷰에 나타납니다.

키워드 인수

함수를 호출할 때, 인수를 이름 붙일 수 있습니다. 이렇게 하면 호출이 읽기 쉬워집니다, 특히 많은 매개변수가 있는 함수의 경우, 그리고 어떤 순서로든 전달할 수 있습니다.

키워드 인수는 함수 호출을 자체 문서화합니다. 위치와 키워드를 섞을 수 있습니다: 위치 인수는 먼저 와야 합니다. 불린 플래그가 많거나 유사한 타입의 많은 매개변수가 있는 함수의 경우, 키워드 인수는 잘못된 순서로 인수를 전달하는 것에서 오는 조용한 실수를 방지합니다.

키워드 인수는 이름으로 바인드되므로, 잘 읽히고 위치를 기억하는 것에서 해방시킵니다. 호출 지점의 규칙: 위치 인수가 먼저, 그 다음 키워드 인수, 그리고 같은 매개변수를 두 가지로 이름 붙이면 TypeError를 일으킵니다. 역방향으로 만드는 것의 가치 있는 디자인 움직임: 맨 *를 자신의 시그니처에 넣어서 호출자가 매개변수를 이름 붙이도록 강제합니다 (def connect(host, *, timeout=30)). 그것은 무료이고 불투명한 connect("db", 30)connect("db", timeout=30)으로 바꿈으로써, 누군가 나중에 중간에 매개변수를 추가하더라도 살아남습니다.

python
def describe_player(name, score, level):
    print(f"{name} | Score: {score} | Level: {level}")

describe_player("민지", 87, 5)                        # 위치
describe_player(name="민지", level=5, score=87)       # 키워드, 어떤 순서든
describe_player("민지", level=5, score=87)            # 섞기: 위치 먼저
Juno키워드 인수 호출에서 인수를 이름 붙입니다 (score=87), 호출이 자신을 설명하고, 어떤 순서로든 전달할 수 있습니다. 한 가지 규칙: 모든 위치 인수는 이름 붙은 것들 앞에 옵니다. 매개변수가 많은 함수에 좋습니다.
Juno키워드 인수 인수를 이름 붙이면 호출이 자체 문서화되고 순서 독립적이며, 위치 인수가 필수적으로 먼저 옵니다. 넓은 시그니처나 불린 플래그의 경우, 키워드 인수를 선호하므로 아무도 위치를 세어서 호출을 읽을 필요가 없습니다.
Juno키워드 인수 키워드 인수는 이름으로 바인드되고 위치 인수를 따라야 합니다. 진정한 레버는 자신의 시그니처입니다: 맨 `*`는 호출자가 그 뒤를 이름 붙이도록 강제하므로 누군가 중간에 새 매개변수를 슬롯하더라도 호출이 여전히 올바르게 읽힙니다.

반환값

return은 호출자에게 값을 보냅니다. return이 없으면 함수는 None을 돌려줍니다. return이 실행되면 함수는 즉시 종료됩니다. 그 블록 뒤의 모든 코드는 건너뜁니다.

return은 함수를 종료하고 호출자에게 값을 전달합니다. 명시적인 return이 없는 함수는 암묵적으로 None을 반환합니다. return은 함수 몸체의 어디든 나타날 수 있으며 여러 번 사용될 수 있습니다. 도달한 첫 번째가 함수를 종료합니다. 이것은 조기 반환을 가드 절에 유용하게 만듭니다.

return은 호출자에게 값을 건네고 함수를 즉시 끝냅니다. 그리고 return 없이 끝에서 떨어져 나가는 함수는 None을 반환합니다. 여러 반환은 좋은 스타일이며, 냄새가 아닙니다: 맨 위의 실패 또는 엣지 케이스에 대한 조기 return("가드 절" 패턴)은 전체 몸체를 큰 if 내에 중첩하는 것보다 훨씬 잘 읽힙니다. 한 가지 미묘함: try 블록 내의 return은 여전히 먼저 일치하는 finally 블록을 실행하므로, finally의 정리는 조기 반환할 때도 일어납니다.

python
def add(a, b):
    return a + b

result = add(3, 4)   # result = 7
print(result)

return은 또한 함수를 즉시 종료합니다. 그 블록 뒤의 모든 코드는 실행되지 않습니다.

Juno반환값return은 함수를 호출한 사람에게 값을 돌려주고, 함수는 바로 거기서 멈추며, 그 뒤의 모든 것은 건너뜁니다. return을 빠뜨리면 None을 돌려받습니다. print는 값을 보여주고, return은 그것을 사용하기 위해 돌려줍니다. 그 차이를 느끼는 데 시간이 걸렸습니다.
Juno반환값return은 값을 전달하고 즉시 종료하며, return이 없으면 None을 의미합니다. 여러 개를 가질 수 있고, 맨 위의 엣지 케이스에 대한 조기 `return`은 일반적으로 모든 것을 큰 `if` 안에 감싸는 것보다 더 깔끔하게 읽힙니다.
Juno반환값return은 그 자리에서 종료되고 끝에서 떨어져 나가는 것은 `None`을 의미합니다. 깊은 중첩 대신 가드 절로서 조기 반환을 사용합니다. 그리고 `try` 내의 `return`은 여전히 `finally` 블록을 실행하므로, 정리는 조기 종료 시에도 발생합니다.

여러 값 반환하기

Python을 사용하면 쉼표로 구분하여 여러 값을 반환할 수 있습니다. 호출자는 그것들을 튜플로 받고 한 줄에서 별도의 이름으로 언팩할 수 있습니다.

쉼표를 사용한 여러 값 반환은 이들을 튜플로 포장합니다. 호출자는 일치하는 이름으로 언팩합니다. 이것은 자연스럽게 하나 이상의 결과를 생성하는 함수에 대한 관용적인 Python입니다. 이것은 특수한 기능이 아닙니다. 이것은 튜플 포장 및 언팩입니다.

return a, b는 값들을 튜플로 포장하고, 호출자는 low, high = f()로 그들을 분해합니다. 두세 개 결과에 대해 깔끔하게 읽힙니다. 그 이상으로 위치는 책임이 됩니다: 함수를 호출하는 아무도 세 번째 항목이 카운트였는지 평균인지 기억하지 못하며, 교환된 쌍은 조용한 버그입니다. 그 지점에서 대신 뭔가 이름지어진 것을 반환합니다, NamedTuple 또는 @dataclass이므로 호출자는 result[2] 대신 result.average를 위해 도달합니다. 어느 쪽이든 tuple[int, str]로 반환을 어노테이션하므로 타입 체커가 호출자가 잘못된 개수의 값을 언팩하는 것을 잡을 수 있습니다.

python
def min_max(numbers):
    return min(numbers), max(numbers)

low, high = min_max([3, 7, 1, 9, 4])
print(low, high)   # 1 9

low, high = ... 신택스는 언팩입니다: Python은 각 반환된 값을 해당하는 이름에 할당합니다.

Juno여러 값 반환하기 쉼표로 값을 분리하면 함수는 그들을 튜플로 함께 반환합니다. 호출자는 한 줄에서 그들을 잡습니다, `low, high = min_max(...)`, 값마다 하나의 이름. 함수가 자연스럽게 하나 이상의 답을 생성할 때마다 편리합니다.
Juno여러 값 반환하기 쉼표는 값들을 튜플로 포장하고, 호출자는 일치하는 이름으로 언팩합니다. 이것은 순수 튜플 포장 및 언팩이며, 특수한 기능이 아닙니다. 두 세 결과에 깔끔합니다.
Juno여러 값 반환하기 쉼표 반환은 튜플을 포장하며, 두세 값에 좋습니다. 그 이상으로, 위치는 아무도 읽을 수 없는 함정이 되므로 `NamedTuple` 또는 `@dataclass`를 반환하고 호출자가 인덱스를 세는 대신 `result.average`를 사용하게 하세요.

스코프

함수 내부에 생성된 변수는 그 함수 내부에만 존재합니다. 외부에서 그들을 볼 수 없습니다. 모든 함수 외부에 정의된 변수는 어디서나 보입니다만, 명시적 선언 없이 함수 내부에서 그들을 변경할 수 없습니다.

함수 내부에 생성된 이름은 로컬입니다: 그것은 그곳에만 살고 함수가 반환할 때 사라집니다. 파일의 맨 위에 정의된 이름은 글로벌입니다. 함수 내부에서 글로벌을 읽는 것은 예식 없이 작동하지만, global name 없이 하나에 할당하려면 Python은 할당을 로컬 생성으로 취급하여 글로벌을 섀도합니다. 실제로는 거의 global을 원하지 않습니다: 값을 인수로 전달하고 return으로 결과를 돌려주는 것은 함수의 효과를 호출 지점에 보이게 유지합니다.

Python은 LEGB 규칙으로 이름을 분해합니다, 네 곳을 순서대로 검사합니다: Local (이 함수), Enclosing (이것을 감싸는 바깥 함수), Global (모듈), 그 다음 Built-in (len 같은 이름). 읽기는 그 체인을 따릅니다; 할당은 항상 로컬을 생성합니다 당신이 다르게 말하지 않는 한. 그 비대칭은 버그 소스입니다: global count 라인 없이 글로벌에 대해 count += 1UnboundLocalError를 일으킵니다. 할당은 count를 함수 전체에 대해 로컬로 표시하고 읽기가 로컬 값이 존재하기 전에 일어나기 때문입니다. global name은 할당을 모듈 레벨로 옮깁니다; nonlocal name은 대신 가장 가까운 엔클로징 함수를 대상으로 하며, 이것이 클로저가 그것을 만든 함수의 변수를 업데이트하는 방법입니다. 둘 다 냄새로 취급하세요: 바깥 상태를 변경하기 위해 도달하는 함수는 입력을 받고 출력을 반환하는 것보다 테스트하기 더 어렵습니다.

Juno스코프 함수 내부에 만든 변수는 그곳에만 살고, 외부는 그것을 볼 수 없습니다. 외부 변수를 내부에서 읽을 수 있지만, 하나를 변경하려면 먼저 global 라인이 필요합니다. 거의 절대 그것에 도달하지 마세요: 값에서 전달하고, 결과에서 돌려받으면 함수가 따라가기 단순하게 유지됩니다.
Juno스코프 함수 내부에 만든 이름은 로컬이고 반환할 때 사라집니다. 글로벌을 읽는 것은 무료이고, 하나를 쓰려면 `global name`이 필요하거나 Python은 그것을 섀도하는 로컬을 만듭니다. 인수에서 전달하고 `return`에서 돌려주는 것을 선호하므로 함수의 효과가 호출에서 보입니다.
Juno스코프 룩업을 위한 LEGB이지만, 할당은 항상 로컬을 생성합니다 당신이 `global` 또는 `nonlocal`을 선언하지 않는 한, 이것이 글로벌에 대해 `count += 1`이 `UnboundLocalError`를 던지는 이유입니다. 두 키워드는 냄새입니다: 바깥 상태를 변경하는 함수는 입력을 받고 출력을 반환하는 것보다 테스트하기 더 어렵습니다.
python
def calculate():
    result = 42   # 이 함수에 로컬
    return result

calculate()
print(result)   # NameError, result는 여기에 존재하지 않음
python
count = 0

def increment():
    global count    # 글로벌을 수정하고 싶다고 선언
    count += 1

increment()
print(count)   # 1

global을 사용하는 것은 마지막 수단이어야 합니다. 그것은 코드를 이해하기 더 어렵게 만듭니다. 값을 전달하고 반환하는 것을 선호합니다. 스코프는 변수와 타입 챕터에서 다룬 할당이 이름을 값에 바인드하는 방법에서 직접 빌드합니다.

*args와 **kwargs

가끔 함수가 받을 인수의 개수를 모르면 어떻게 될까요. *args는 임의의 개수의 위치 인수를 튜플로 수집합니다. **kwargs는 임의의 개수의 키워드 인수를 딕셔너리로 수집합니다. argskwargs 이름은 관례이며, 별은 중요합니다.

*args는 초과 위치 인수를 튜플로 수집합니다. **kwargs는 초과 키워드 인수를 딕셔너리로 수집합니다. 둘 다 일반 매개변수와 결합될 수 있습니다. 일반 매개변수가 먼저, *args가 그 다음, 키워드 전용 매개변수, **kwargs가 그 다음입니다. 그들은 인수를 다른 함수에 전달하는 래퍼 함수에 유용합니다.

*args는 남은 위치 인수를 tuple로 수집하고, **kwargs는 남은 키워드 인수를 dict로 수집합니다. 미러 이미지는 호출 지점에서 일어납니다: func(*some_list)는 리스트를 위치 인수로 펼치고 func(**some_dict)는 딕셔너리를 키워드 인수로 펼칩니다. 그 대칭은 패스-스루 래퍼가 작동하게 합니다, def timed(*args, **kwargs): return inner(*args, **kwargs)는 단일 매개변수를 이름 붙이지 않고 그것이 받은 것을 무엇이든 앞으로 전달합니다. 유용하지만, 읽기 가능한 시그니처를 대가로 치릅니다: *args, **kwargs로 모든 것을 삼키는 래퍼는 호출자와 타입 체커에게 그것이 받는 것에 대해 아무것도 보여주지 않으므로 실제로 임의의 인수를 전달해야 할 때 그것에 도달하세요, 일반 함수의 기본 모양이 아닙니다.

python
def total(*args):
    return sum(args)

total(1, 2, 3)          # 6
total(1, 2, 3, 4, 5)   # 15
python
def display(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")

display(name="민지", score=87, level=5)

이들을 일반 매개변수와 섞을 수 있습니다. 일반 매개변수가 먼저 옵니다:

python
def describe(title, *tags, **metadata):
    print(f"{title} | tags: {tags} | meta: {metadata}")

describe("Python intro", "beginner", "python", author="민지", year=2024)
Juno*args와 **kwargs*args는 추가 위치 인수의 임의 개수를 튜플로 퍼담고, **kwargs는 추가된 이름 붙은 것들을 딕셔너리로 퍼담습니다. argskwargs 단어는 관례일 뿐이며, `*`와 `**`은 실제 일을 합니다. 얼마나 많은 것이 들어오는지 미리 말할 수 없을 때 편리합니다.
Juno*args와 **kwargs*args는 추가 위치 인수를 튜플로 수집하고, **kwargs는 추가 키워드 인수를 딕셔너리로 수집합니다. 시그니처의 순서: 일반 매개변수, `*args`, 키워드 전용, `**kwargs`. 인수를 다른 함수에 곧바로 전달하는 래퍼에 가장 유용합니다.
Juno*args와 **kwargs `*`와 `**`은 시그니처에서 수집하고 호출 지점에서 펼칩니다, 이것이 래퍼가 `inner(*args, **kwargs)`로 모든 것을 전달하는 것을 가능하게 합니다. 대가는 호출자에게 아무것도 말해주지 않는 시그니처이므로, 실제 패스-스루를 유지하고, 일반 함수가 아닙니다.

문자열 표현

문자열 표현은 함수 맨 위의 문자열로, 그것이 무엇을 하는지 설명합니다. Python 에디터와 도구는 그것을 사용하여 함수 호출을 호버할 때 도움말을 보여줍니다. 삼중 따옴표를 사용하고, 단순 함수는 한 줄을 작성합니다.

문자열 표현은 함수 몸체의 첫 번째 것으로, 삼중 따옴표로 된 문자열입니다. 도구는 그것을 읽습니다: help(func)는 그것을 인쇄하고, 에디터는 호출을 호버할 때 그것을 보여주므로, 함수가 무엇을 하는지 잊었을 때 정확히 그것이 지불됩니다. 관례는 하나의 요약 라인이고, 더 자세히 필요하면 빈 라인과 더 자세합니다. 두 개 이상의 장소에서 호출되는 모든 것에서 필수로 취급하고, 이름이 이미 모든 것을 말해주는 일회용 헬퍼에서는 선택입니다.

문자열 표현은 함수, 클래스 또는 모듈 몸체의 첫 문입니다, 그리고 그것은 함수가 무엇을 하는지 그리고 무엇을 반환하는지 말해야 하지, 읽는 사람이 이미 볼 수 있는 매개변수 리스트를 재상태하지 마세요. 그것은 시그니처에서 보이지 않는 것에서 스스로를 번다: 엣지 케이스, 그것이 일으키는 것, 빈 입력이 무엇을 하는지. 시그니처의 타입 힌트가 이미 타입을 전달할 때 형식적인 인수별 블록을 건너뛰세요, 그 상세는 힌트에 속합니다, 날짜를 벗어난 산문에 중복되지 마세요. 목표는 6개월 전에 작성한 호출을 디버깅하는 오전 2시에 읽기를 원하는 라인입니다.

python
def normalise(value, min_val, max_val):
    """값을 알려진 최소값과 최대값을 감안하여 0-1 범위로 스케일합니다."""
    return (value - min_val) / (max_val - min_val)
python
def build_url(base, version, resource, *, secure=True):
    """
    API 엔드포인트 URL을 빌드합니다.

    완전히 정규화된 URL 문자열을 반환합니다. secure이 False이면,
    URL은 https 대신 http를 사용합니다.
    """
    scheme = "https" if secure else "http"
    base = base.replace("https://", "").replace("http://", "")
    return f"{scheme}://{base}/{version}/{resource}"

목적이 이름과 시그니처에서 명확하지 않은 모든 함수에 대해 문자열 표현을 작성하세요.

Juno문자열 표현 문자열 표현은 삼중 따옴표로 함수 맨 위의 문자열이며, 무엇을 하는지 설명합니다. 에디터는 나중에 함수를 호버할 때 그것을 보여주며, 이것이 당신이 기뻐할 것입니다. 이름과 입력이 이미 목적을 명확히 하지 않을 때마다 작성하세요.
Juno문자열 표현 몸체의 첫 라인, 삼중 따옴표: help()와 에디터는 호버에서 그것을 표면화합니다. 요약 라인 하나는 대부분의 함수를 다루고, 행동이 그것을 필요할 때만 아래에 자세히 추가합니다. 여러 장소에서 호출되는 것에 필수이고, 이름이 그 모든 것을 말해주는 한-라이너에 건너뛸 수 있습니다.
Juno문자열 표현 함수가 무엇을 하는지, 무엇을 반환하는지, 엣지 케이스를 말하고, 읽는 사람이 이미 볼 수 있는 재상태된 매개변수 리스트가 아닙니다. 타입 힌트가 타입을 전달하도록 하므로 당신이 같은 사실 두 번 유지하지 않습니다. 오전 2시에 6개월 전에 작성한 호출을 디버깅할 때 원할 라인을 목표로 합니다.

타입 힌트

타입 힌트를 사용하면 함수가 기대하고 반환하는 타입을 어노테이션할 수 있습니다. Python은 런타임에 그것들을 강제하지 않지만, 에디터는 아무것도 실행하기 전에 실수를 잡기 위해 그것들을 사용합니다. ->는 콜론 앞의 반환 타입을 지정합니다.

타입 힌트는 도구가 확인하는 문서입니다. 에디터와 타입 체커 (mypy, pyright)는 런타임 전에 타입 불일치를 잡기 위해 그것들을 사용합니다. 그들은 표준 Python의 런타임 효과가 없습니다. -> None은 반환값이 없는 함수의 올바른 어노테이션입니다. 일반 컨테이너의 경우, list[int], dict[str, int]를 사용합니다 (Python 3.9+).

타입 힌트는 런타임에 아무것도 변경하지 않으며, Python은 그것들과 함께 또는 없이 동일하게 실행됩니다. 그들의 전체 값은 프로그램 외부 레이어입니다: 타입 체커 (mypy 또는 pyright)는 실행 전에 코드를 읽고, 에디터는 타이핑할 때 나쁜 호출을 플래그합니다. 따라서 거짓말하는 힌트는 체커가 그것을 신뢰하기 때문에 힌트가 없는 것보다 나쁩니다. list[int], dict[str, int]str | None을 직접 사용합니다 (최신 Python에는 typing 임포트가 필요하지 않음), 그리고 아무것도 반환하지 않는 함수를 -> None으로 어노테이션합니다, 이것은 또한 식에서 호출하는 것 (x = log(...))이 실수임을 신호합니다. 페이오프는 많은 장소에서 호출되는 함수에 랜드합니다: 체커는 호출자가 잘못된 타입을 전달하는 것을 잡습니다 런타임에 3 프레임 깊이의 혼동스러운 실패로 표면화되는 대신.

python
def greet(name: str, score: int) -> str:
    return f"{name} scored {score}"
python
def log(message: str) -> None:
    print(f"[LOG] {message}")
python
def top_scores(scores: list[int], n: int) -> list[int]:
    return sorted(scores, reverse=True)[:n]

타입 힌트는 선택입니다만 여러 장소에서 호출되는 모든 함수에 가치가 있습니다. 그들은 도구가 확인할 수 있는 문서입니다.

Juno타입 힌트 타입 힌트는 함수가 무엇을 취하고 제공하는지 기록합니다, `name: str`과 반환을 위한 `-> str` 같은. Python은 실행할 때 그것들을 강제하지 않지만, 에디터는 그것들을 읽고 실행 전에 경고합니다. 도구가 검사할 수 있는 노트로 생각하세요.
Juno타입 힌트 힌트는 타입 체커가 확인할 수 있는 문서이며, 런타임 효과가 없습니다. `-> None`은 함수가 아무것도 반환하지 않을 때 올바른 어노테이션이고, `list[int]` / `dict[str, int]`는 최신 Python에서 `typing` 임포트 없이 작동합니다. 여러 장소에서 호출되는 것을 위해 가치가 있습니다.
Juno타입 힌트 힌트는 런타임에 아무것도 하지 않으며, 그들의 값은 체커와 에디터가 읽는 것이므로, 거짓말하는 힌트는 없는 것보다 나쁩니다. `str | None` 및 `list[int]`을 직접 사용하고, 반환이 없을 때 `-> None`을 사용합니다. 승리는 3 프레임 깊이로 실패하기 전에 잘못된 타입의 호출자를 잡는 것입니다.

값으로서의 함수

Python의 함수는 문자열이나 숫자처럼 값입니다. 그들을 변수에 할당하고 다른 함수에 전달할 수 있습니다. 이것이 sorted()key= 함수를 받아들이는 방법입니다.

함수는 첫 번째 클래스 객체입니다: 그들은 타입 (function)을 가지고, 변수와 컬렉션에 저장될 수 있으며, 인수로 전달되거나 값으로 반환될 수 있습니다. 이것은 sorted(key=...), map()filter()와 같은 고차 함수의 재단입니다.

함수는 참조로 전달하는 객체이며, 절대 사본이 아니므로, 하나를 다른 함수에 건네는 것은 저렴합니다. 이것 밖에서 자라는 패턴은 클로저입니다: 다른 함수 내에 정의되고 바깥 함수가 반환한 후에도 바깥 함수의 변수를 여전히 도달하는 함수입니다. 온더플라이로 구성된 함수를 빌드하는 방법입니다, make_multiplier(3)은 3을 곱하는 함수를 반환하며, 3은 캡처되고 유지됩니다. 실제 코드의 두 주의: 클로저는 캡처 시간의 값이 아니라 변수를 캡처합니다, 이것은 루프에서 클로저를 빌드하는 사람들을 잡습니다 (그들은 모두 루프의 최종값을 봅니다); 그리고 많은 반환된 함수를 쌓으면 호출 경로를 추적하기 어렵게 만들므로, 그들이 명확하게 읽을 때 클로저를 사용하고, 상태가 자라날 때 클래스에 도달합니다.

python
def double(x):
    return x * 2

def apply(func, value):
    return func(value)

apply(double, 5)   # 10

함수를 인수로 전달하는 것은 sorted(), map()filter()로 지속적으로 나타납니다. 그리고 람다와 이해 챕터에서도 그것을 볼 것입니다.

Juno값으로서의 함수 함수는 문자열이나 숫자처럼 값이므로, 그것을 변수에 저장하고 다른 함수에 건넬 수 있습니다. 이것이 정확히 당신이 함수를 `key=`로 `sorted()`에 전달하는 것을 가능하게 합니다. 처음에는 이상했으나, 그것이 명확해지고 어디서나 나타났습니다.
Juno값으로서의 함수 함수는 첫 번째 클래스 객체입니다: 그것들을 할당하고, 리스트에 저장하고, 전달하고, 반환합니다. 이것은 `sorted(key=...)`, `map()` 및 `filter()` 뒤의 엔진이며, 나중에 와이어할 모든 콜백 뒤의 것입니다.
Juno값으로서의 함수 함수를 전달하는 것은 참조이며, 복사본이 아니며, 클로저는 바깥 함수가 반환한 후에도 내부 함수가 바깥 함수의 변수에 도달하게 합니다. 그것은 값이 아니라 변수를 캡처하므로, 루프에서 클로저를 빌드하는 것은 매번 물어뜯습니다. 캡처된 상태가 자라날 때, 클래스는 더 잘 읽힙니다.

실제로

함께 작동하는 두 함수: letter_grade는 점수를 글자로 변환하고, summarise는 리스트의 모든 점수에 대해 그것을 호출합니다:

python
def letter_grade(score: int) -> str:
    if score >= 90:
        return "A"
    elif score >= 80:
        return "B"
    elif score >= 70:
        return "C"
    else:
        return "F"

def summarise(scores: list[int]) -> None:
    total = sum(scores)
    avg = total / len(scores)
    grades = []
    for s in scores:
        grades.append(letter_grade(s))
    print(f"Average: {avg:.1f}")
    print(f"Grades: {', '.join(grades)}")

summarise([87, 92, 74, 65, 91])

로그 포매터와 그것을 사용하는 파일 프로세서이며, dry_run 기본 매개변수는 명시적으로 비활성화되지 않는 한 부작용을 방지합니다:

python
def format_log(level: str, message: str) -> str:
    return f"[{level.upper():5}] {message}"

def process_file(path: str, dry_run: bool = True) -> bool:
    print(format_log("info", f"Processing {path}"))
    if dry_run:
        print(format_log("info", "Dry run, no changes made"))
        return True
    return True

process_file("report.csv")
process_file("report.csv", dry_run=False)

단일 값 정규화기와 그 위에 빌드된 열 정규화기이며, 타입 힌트와 문자열 표현이 있습니다. 열 함수는 범위를 한 번 계산하고 각 항목에 대해 스칼라 함수를 재사용합니다:

python
def normalise(value: float, min_val: float, max_val: float) -> float:
    """알려진 최소값과 최대값을 감안하여 값을 0-1 범위로 스케일합니다."""
    if max_val == min_val:
        return 0.0
    return (value - min_val) / (max_val - min_val)

def normalise_column(values: list[float]) -> list[float]:
    """값의 전체 열을 정규화합니다."""
    lo, hi = min(values), max(values)
    return [normalise(v, lo, hi) for v in values]

raw = [10.0, 25.0, 5.0, 40.0, 15.0]
print(normalise_column(raw))

타입 힌트는 여기에 두 목적을 제공합니다: 함수가 기대하는 것을 문서화하고, 타입 체커가 호출자가 문자열 리스트를 실수로 전달하는 것을 잡게 합니다.