4. 기타 제어 흐름 도구¶
방금 소개한 while
문 외에도, 파이썬은 다른 언어들에서 알려진 일반적인 제어 흐름 문들을 알고 있으며, 나름의 변형을 가하고 있습니다.
4.1. if
문¶
아마도 가장 잘 알려진 문장 형은 if
문일 것입니다. 예를 들어:
>>> x = int(input("Please enter an integer: "))
Please enter an integer: 42
>>> if x < 0:
... x = 0
... print('Negative changed to zero')
... elif x == 0:
... print('Zero')
... elif x == 1:
... print('Single')
... else:
... print('More')
...
More
없거나 여러 개의 elif
부가 있을 수 있고, else
부는 선택적입니다. 키워드 〈elif
〉 는 〈else if〉 의 줄임 표현인데, 과도한 들여쓰기를 피하는 데 유용합니다. if
… elif
… elif
… 시퀀스는 다른 언어들에서 발견되는 switch
나 case
문을 대신합니다.
4.2. for
문¶
파이썬에서 for
문은 C 나 파스칼에서 사용하던 것과 약간 다릅니다. (파스칼처럼) 항상 숫자의 산술적인 진행을 통해 이터레이션 하거나, (C처럼) 사용자가 이터레이션 단계와 중지 조건을 정의할 수 있도록 하는 대신, 파이썬의 for
문은 임의의 시퀀스 (리스트나 문자열)의 항목들을 그 시퀀스에 들어있는 순서대로 이터레이션 합니다. 예를 들어 (말장난이 아니라):
>>> # Measure some strings:
... words = ['cat', 'window', 'defenestrate']
>>> for w in words:
... print(w, len(w))
...
cat 3
window 6
defenestrate 12
루프 안에서 이터레이트하는 시퀀스를 수정할 필요가 있다면 (예를 들어, 선택한 항목들을 중복시키기), 먼저 사본을 만들 것을 권합니다. 시퀀스를 이터레이트할 때 묵시적으로 사본이 만들어지지는 않습니다. 슬라이스 표기법은 이럴 때 특히 편리합니다:
>>> for w in words[:]: # Loop over a slice copy of the entire list.
... if len(w) > 6:
... words.insert(0, w)
...
>>> words
['defenestrate', 'cat', 'window', 'defenestrate']
for w in words:
를 쓰면, 위의 예는 defenestrate
를 반복해서 넣고 또 넣음으로써, 무한한 리스트를 만들려고 시도하게 됩니다.
4.3. range()
함수¶
숫자들의 시퀀스로 이터레이트할 필요가 있으면, 내장 함수 range()
가 편리합니다. 수열을 만듭니다:
>>> for i in range(5):
... print(i)
...
0
1
2
3
4
끝값은 만들어지는 수열에 포함되지 않습니다; range(10)
은 10개의 값을 만드는데, 길이 10인 시퀀스의 항목들을 가리키는 올바른 인덱스들입니다. 범위가 다른 숫자로 시작하거나, 다른 증가분을 (음수조차 가능합니다; 때로 이것을 〈스텝(step)〉이라고 부릅니다) 지정하는 것도 가능합니다:
range(5, 10)
5, 6, 7, 8, 9
range(0, 10, 3)
0, 3, 6, 9
range(-10, -100, -30)
-10, -40, -70
시퀀스의 인덱스들로 이터레이트 하려면, 다음처럼 range()
와 len()
을 결합할 수 있습니다:
>>> a = ['Mary', 'had', 'a', 'little', 'lamb']
>>> for i in range(len(a)):
... print(i, a[i])
...
0 Mary
1 had
2 a
3 little
4 lamb
하지만, 그럴 때 대부분은, enumerate()
함수를 쓰는 것이 편리합니다, 루프 테크닉 를 보세요.
범위를 그냥 인쇄하면 이상한 일이 일어납니다:
>>> print(range(10))
range(0, 10)
많은 경우에 range()
가 돌려준 객체는 리스트인 것처럼 동작하지만, 사실 리스트가 아닙니다. 이터레이트할 때 원하는 시퀀스 항목들을 순서대로 돌려주는 객체이지만, 실제로 리스트를 만들지 않아서 공간을 절약합니다.
이런 객체를 이터러블 이라고 부릅니다. 공급이 소진될 때까지 일련의 항목들을 얻을 수 있는 무엇인가를 기대하는 함수와 구조물들의 타깃으로 적합합니다. 우리는 for
문이 그런 구조물임을 보았습니다. 함수 list()
도 그런 것입니다; 이터러블로 리스트를 만듭니다:
>>> list(range(5))
[0, 1, 2, 3, 4]
나중에 이터러블을 돌려주고 이터러블을 인자로 받는 함수들을 더 보게 됩니다.
4.4. 루프의 break
와 continue
문, 그리고 else
절¶
break
문은, C처럼, 가장 가까이서 둘러싸는 for
나 while
루프로부터 빠져나가게 만듭니다.
루프 문은 else
절을 가질 수 있습니다; 루프가 리스트의 소진이나 (for
의 경우) 조건이 거짓이 돼서 (while
의 경우) 종료할 때 실행됩니다. 하지만 루프가 break
문으로 종료할 때는 실행되지 않습니다. 소수를 찾는 루프를 통해 다음에서 예시합니다:
>>> for n in range(2, 10):
... for x in range(2, n):
... if n % x == 0:
... print(n, 'equals', x, '*', n//x)
... break
... else:
... # loop fell through without finding a factor
... print(n, 'is a prime number')
...
2 is a prime number
3 is a prime number
4 equals 2 * 2
5 is a prime number
6 equals 2 * 3
7 is a prime number
8 equals 2 * 4
9 equals 3 * 3
(이것은 올바른 코드입니다. 자세히 들여다보면: else
절은 if
문이 아니라 for
루프에 속합니다.)
루프와 함께 사용될 때, else
절은 if
문보다는 try
문의 else
절과 비슷한 면이 많습니다: try
문의 else
절은 예외가 발생하지 않을 때 실행되고, 루프의 else
절은 break
가 발생하지 않을 때 실행됩니다. try
문과 예외에 관한 자세한 내용은 예외 처리하기 를 보세요.
continue
문은, 역시 C에서 빌렸습니다, 루프의 다음 이터레이션에서 계속하도록 만듭니다:
>>> for num in range(2, 10):
... if num % 2 == 0:
... print("Found an even number", num)
... continue
... print("Found a number", num)
Found an even number 2
Found a number 3
Found an even number 4
Found a number 5
Found an even number 6
Found a number 7
Found an even number 8
Found a number 9
4.5. pass
문¶
pass
문은 아무것도 하지 않습니다. 문법적으로 문장이 필요하지만, 프로그램이 특별히 할 일이 없을 때 사용할 수 있습니다. 예를 들어:
>>> while True:
... pass # Busy-wait for keyboard interrupt (Ctrl+C)
...
최소한의 클래스를 만들 때 흔히 사용된다:
>>> class MyEmptyClass:
... pass
...
pass
가 사용될 수 있는 다른 장소는 새 코드를 작업할 때 함수나 조건부 바디의 자리를 채우는 것인데, 여러분이 더 추상적인 수준에서 생각할 수 있게 합니다. pass
는 조용히 무시됩니다:
>>> def initlog(*args):
... pass # Remember to implement this!
...
4.6. 함수 정의하기¶
피보나치 수열을 임의의 한도까지 출력하는 함수를 만들 수 있습니다:
>>> def fib(n): # write Fibonacci series up to n
... """Print a Fibonacci series up to n."""
... a, b = 0, 1
... while a < n:
... print(a, end=' ')
... a, b = b, a+b
... print()
...
>>> # Now call the function we just defined:
... fib(2000)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597
키워드 def
는 함수 정의 를 시작합니다. 함수 이름과 괄호로 싸인 형식 파라미터들의 목록이 뒤따릅니다. 함수의 바디를 형성하는 문장들이 다음 줄에서 시작되고, 반드시 들여쓰기 되어야 합니다.
함수 바디의 첫 번째 문장은 선택적으로 문자열 리터럴이 될 수 있습니다; 이 문자열 리터럴은 함수의 토큐멘테이션 문자열, 즉 독스트링 (docstring) 입니다. (독스트링에 대한 자세한 내용은 도큐멘테이션 문자열 에 나옵니다.) 독스트링을 사용해서 온라인이나 인쇄된 도큐멘테이션을 자동 생성하거나, 사용자들이 대화형으로 코드를 열람할 수 있도록 하는 도구들이 있습니다; 여러분이 작성하는 코드에 독스트링을 첨부하는 것은 좋은 관습입니다, 그러니 버릇을 들이는 것이 좋습니다.
함수의 실행 은 함수의 지역 변수들을 위한 새 심볼 테이블을 만듭니다. 좀 더 구체적으로, 함수에서의 모든 변수 대입들은 값을 지역 심볼 테이블에 저장합니다; 반면에 변수 참조는 먼저 지역 심볼 테이블을 본 다음, 전역 심볼 테이블을 본 후, 마지막으로 내장 이름들의 테이블을 살핍니다. 그래서, 참조될 수는 있다 하더라도, 전역 변수들은 함수 내에서 (global
문으로 명시하지 않는 이상) 직접 값이 대입될 수 없습니다.
함수 호출로 전달되는 실제 파라미터들 (인자들)은 호출될 때 호출되는 함수의 지역 심볼 테이블에 만들어집니다; 그래서 인자들은 값에 의한 호출 (call by value) 로 전달됩니다 (값 은 항상 객체의 값이 아니라 객체 참조 입니다). 1 함수가 다른 함수를 호출할 때, 그 호출을 위한 새 지역 심볼 테이블이 만들어집니다.
함수 정의는 현재 심볼 테이블에 함수 이름을 만듭니다. 함수 이름의 값은 인터프리터가 사용자 정의 함수로 인식하는 형입니다. 이 값은 다른 이름에 대입될 수 있는데, 이 역시 함수로 사용될 수 있습니다. 이것이 이름을 바꾸는 일반적인 방법입니다:
>>> fib
<function fib at 10042ed0>
>>> f = fib
>>> f(100)
0 1 1 2 3 5 8 13 21 34 55 89
다른 언어들을 사용했다면, fib
가 값을 돌려주지 않기 때문에 함수가 아니라 프로시저라고 생각할 수 있습니다. 사실, return
문이 없는 함수도 값을 돌려줍니다, 비록 따분한 값이기는 하지만. 이 값은 None
이라고 불립니다 (내장 이름입니다). None
이 출력할 유일한 값이라면, 인터프리터는 보통 None
값 출력을 억제합니다. 꼭 보길 원한다면 print()
를 사용할 수 있습니다:
>>> fib(0)
>>> print(fib(0))
None
인쇄하는 대신, 피보나치 수열의 숫자들 리스트를 돌려주는 함수를 작성하는 것도 간단합니다:
>>> def fib2(n): # return Fibonacci series up to n
... """Return a list containing the Fibonacci series up to n."""
... result = []
... a, b = 0, 1
... while a < n:
... result.append(a) # see below
... a, b = b, a+b
... return result
...
>>> f100 = fib2(100) # call it
>>> f100 # write the result
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
여느 때처럼, 이 예는 몇 가지 새 파이썬 기능을 보여줍니다:
return
문은 함수로부터 값을 갖고 복귀하게 만듭니다. 표현식 인자 없는return
은None
을 돌려줍니다. 함수의 끝으로 떨어지면 역시None
을 돌려줍니다.문장
result.append(a)
은 리스트 객체result
의 메서드 를 호출합니다. 메서드는 객체에 〈속하는〉 함수이고obj.methodname
라고 이름 붙여지는데,obj
는 어떤 객체이고 (표현식이 될 수 있습니다),methodname
는 객체의 형에 의해 정의된 메서드의 이름입니다. 다른 형은 다른 메서드들을 정의합니다. 서로 다른 형들의 메서드는 모호함 없이 같은 이름을 가질 수 있습니다. (클래스 를 사용해서 여러분 자신의 형과 메서드를 정의하는 것이 가능합니다, 클래스 를 보세요) 예에 나오는 메서드append()
는 리스트 객체들에 정의되어 있습니다; 요소를 리스트의 끝에 덧붙입니다. 이 예에서는result = result + [a]
와 동등하지만, 더 효율적입니다.
4.7. 함수 정의 더 보기¶
정해지지 않은 개수의 인자들로 함수를 정의하는 것도 가능합니다. 세 가지 형식이 있는데, 조합할 수 있습니다.
4.7.1. 기본 인자 값¶
가장 쓸모 있는 형식은 하나나 그 이상 인자들의 기본값을 지정하는 것입니다. 정의된 것보다 더 적은 개수의 인자들로 호출될 수 있는 함수를 만듭니다. 예를 들어:
def ask_ok(prompt, retries=4, reminder='Please try again!'):
while True:
ok = input(prompt)
if ok in ('y', 'ye', 'yes'):
return True
if ok in ('n', 'no', 'nop', 'nope'):
return False
retries = retries - 1
if retries < 0:
raise ValueError('invalid user response')
print(reminder)
이 함수는 여러 가지 방법으로 호출될 수 있습니다:
오직 꼭 필요한 인자만 전달해서:
ask_ok('정말 끝내길 원하세요?')
선택적 인자 하나를 제공해서:
ask_ok('파일을 덮어써도 좋습니까?', 2)
또는 모든 인자를 제공해서:
ask_ok('파일을 덮어써도 좋습니까?', 2, '자, 예나 아니요로만 답하세요!')
이 예는 in
키워드도 소개하고 있습니다. 시퀀스가 어떤 값을 가졌는지 아닌지를 검사합니다.
기본값은 함수 정의 시점에 정의되고 있는 스코프에서 구해집니다, 그래서
i = 5
def f(arg=i):
print(arg)
i = 6
f()
는 5
를 인쇄한다.
중요한 주의사항: 기본값은 오직 한 번만 값이 구해집니다. 이것은 기본값이 리스트나 딕셔너리나 대부분 클래스의 인스턴스와 같은 가변 객체일 때 차이를 만듭니다. 예를 들어, 다음 함수는 계속되는 호출로 전달된 인자들을 누적합니다:
def f(a, L=[]):
L.append(a)
return L
print(f(1))
print(f(2))
print(f(3))
다음과 같은 것을 인쇄합니다
[1]
[1, 2]
[1, 2, 3]
연속된 호출 간에 기본값이 공유되지 않기를 원한다면, 대신 함수를 이런 식으로 쓸 수 있습니다:
def f(a, L=None):
if L is None:
L = []
L.append(a)
return L
4.7.2. 키워드 인자¶
함수는 kwarg=value
형식의 키워드 인자 를 사용해서 호출될 수 있습니다. 예를 들어, 다음 함수는:
def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
print("-- This parrot wouldn't", action, end=' ')
print("if you put", voltage, "volts through it.")
print("-- Lovely plumage, the", type)
print("-- It's", state, "!")
하나의 필수 인자 (voltage
) 와 세 개의 선택적 인자 (state
, action
, type
) 를 받아들입니다. 이 함수는 다음과 같은 방법 중 아무것으로나 호출될 수 있습니다.
parrot(1000) # 1 positional argument
parrot(voltage=1000) # 1 keyword argument
parrot(voltage=1000000, action='VOOOOOM') # 2 keyword arguments
parrot(action='VOOOOOM', voltage=1000000) # 2 keyword arguments
parrot('a million', 'bereft of life', 'jump') # 3 positional arguments
parrot('a thousand', state='pushing up the daisies') # 1 positional, 1 keyword
하지만 다음과 같은 호출들은 모두 올바르지 않습니다:
parrot() # required argument missing
parrot(voltage=5.0, 'dead') # non-keyword argument after a keyword argument
parrot(110, voltage=220) # duplicate value for the same argument
parrot(actor='John Cleese') # unknown keyword argument
함수 호출에서, 키워드 인자는 위치 인자 뒤에 나와야 합니다. 전달된 모든 키워드 인자는 함수가 받아들이는 인자 중 하나와 맞아야 하며 (예를 들어, actor
는 parrot
함수의 올바른 인자가 아니다), 그 순서는 중요하지 않습니다. 이것들에는 필수 인자들도 포함됩니다 (예를 들어, parrot(voltage=1000)
도 올바릅니다). 어떤 인자도 두 개 이상의 값을 받을 수 없습니다. 여기, 이 제약 때문에 실패하는 예가 있습니다:
>>> def function(a):
... pass
...
>>> function(0, a=0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: function() got multiple values for keyword argument 'a'
**name
형식의 마지막 형식 파라미터가 존재하면, 형식 파라미터들에 대응하지 않는 모든 키워드 인자들을 담은 딕셔너리 (매핑 형 — dict 를 보세요) 를 받습니다. 이것은 *name
(다음 서브섹션에서 설명한다) 형식의 형식 파라미터와 조합될 수 있는데, 형식 파라미터 목록 밖의 위치 인자들을 담은 튜플을 받습니다. (*name
은 **name
앞에 나와야 합니다.) 예를 들어, 이런 함수를 정의하면:
def cheeseshop(kind, *arguments, **keywords):
print("-- Do you have any", kind, "?")
print("-- I'm sorry, we're all out of", kind)
for arg in arguments:
print(arg)
print("-" * 40)
for kw in keywords:
print(kw, ":", keywords[kw])
이런 식으로 호출될 수 있습니다:
cheeseshop("Limburger", "It's very runny, sir.",
"It's really very, VERY runny, sir.",
shopkeeper="Michael Palin",
client="John Cleese",
sketch="Cheese Shop Sketch")
그리고 당연히 이렇게 인쇄합니다:
-- Do you have any Limburger ?
-- I'm sorry, we're all out of Limburger
It's very runny, sir.
It's really very, VERY runny, sir.
----------------------------------------
shopkeeper : Michael Palin
client : John Cleese
sketch : Cheese Shop Sketch
인쇄되는 키워드 인자들의 순서 함수 호출로 전달된 순서와 일치함이 보장됨에 주목하세요.
4.7.3. 임의의 인자 목록¶
마지막으로, 가장 덜 사용되는 옵션은 함수가 임의의 개수 인자로 호출될 수 있도록 지정하는 것입니다. 이 인자들은 튜플로 묶입니다 (튜플과 시퀀스 을 보세요). 가변 길이 인자 앞에, 없거나 여러 개의 일반 인자들이 올 수 있습니다.
def write_multiple_items(file, separator, *args):
file.write(separator.join(args))
보통, 이 가변 길이
인자들은 형식 파라미터 목록의 마지막에 옵니다, 함수로 전달된 남은 입력 인자들 전부를 그러모으기 때문입니다. *args
파라미터 뒤에 등장하는 형식 파라미터들은 모두 〈키워드-전용〉 인자들인데, 위치 인자 대신 키워드 인자로만 사용될 수 있다는 뜻입니다.
>>> def concat(*args, sep="/"):
... return sep.join(args)
...
>>> concat("earth", "mars", "venus")
'earth/mars/venus'
>>> concat("earth", "mars", "venus", sep=".")
'earth.mars.venus'
4.7.4. 인자 목록 언 패킹¶
인자들이 이미 리스트나 튜플에 있지만, 분리된 위치 인자들을 요구하는 함수 호출을 위해 언 패킹 해야 하는 경우 반대 상황이 벌어집니다. 예를 들어, 내장 range()
함수는 별도의 start 와 stop 인자를 기대합니다. 그것들이 따로 있지 않으면, 리스트와 튜플로부터 인자를 언 패킹하기 위해 *
-연산자를 사용해서 함수를 호출하면 됩니다:
>>> list(range(3, 6)) # normal call with separate arguments
[3, 4, 5]
>>> args = [3, 6]
>>> list(range(*args)) # call with arguments unpacked from a list
[3, 4, 5]
같은 방식으로 딕셔너리도 **
-연산자를 써서 키워드 인자를 전달할 수 있습니다:
>>> def parrot(voltage, state='a stiff', action='voom'):
... print("-- This parrot wouldn't", action, end=' ')
... print("if you put", voltage, "volts through it.", end=' ')
... print("E's", state, "!")
...
>>> d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
>>> parrot(**d)
-- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !
4.7.5. 람다 표현식¶
lambda
키워드들 사용해서 작고 이름 없는 함수를 만들 수 있습니다. 이 함수는 두 인자의 합을 돌려줍니다: lambda a, b: a+b
. 함수 객체가 있어야 하는 곳이면 어디나 람다 함수가 사용될 수 있습니다. 문법적으로는 하나의 표현식으로 제한됩니다. 의미적으로는, 일반적인 함수 정의의 편의 문법일 뿐입니다. 중첩된 함수 정의처럼, 람다 함수는 둘러싸는 스코프에 있는 변수들을 참조할 수 있습니다:
>>> def make_incrementor(n):
... return lambda x: x + n
...
>>> f = make_incrementor(42)
>>> f(0)
42
>>> f(1)
43
위의 예는 함수를 돌려주기 위해 람다 표현식을 사용합니다. 또 다른 용도는 작은 함수를 인자로 전달하는 것입니다:
>>> pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
>>> pairs.sort(key=lambda pair: pair[1])
>>> pairs
[(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]
4.7.6. 도큐멘테이션 문자열¶
여기에 도큐멘테이션 문자열의 내용과 포매팅에 관한 몇 가지 관례가 있습니다.
첫 줄은 항상 객체의 목적을 짧고, 간결하게 요약해야 합니다. 간결함을 위해, 객체의 이름이나 형을 명시적으로 언급하지 않아야 하는데, 이것들은 다른 방법으로 제공되기 때문입니다 (이름이 함수의 작업을 설명하는 동사라면 예외입니다). 이 줄은 대문자로 시작하고 마침표로 끝나야 합니다.
도큐멘테이션 문자열에 여러 줄이 있다면, 두 번째 줄은 비어있어서, 시각적으로 요약과 나머지 설명을 분리해야 합니다. 뒤따르는 줄들은 하나나 그 이상의 문단으로, 객체의 호출 규약, 부작용 등을 설명해야 합니다.
파이썬 파서는 여러 줄 문자열 리터럴에서 들여쓰기를 제거하지 않기 때문에, 도큐멘테이션을 처리하는 도구들은 필요하면 들여쓰기를 제거합니다. 이것은 다음과 같은 관례를 사용합니다. 문자열의 첫줄 뒤에 오는 첫 번째 비어있지 않은 줄이 전체 도튜멘테이션 문자열의 들여쓰기 수준을 결정합니다. (우리는 첫 줄을 사용할 수 없는데, 일반적으로 문자열을 시작하는 따옴표에 붙어있어서 들여쓰기가 문자열 리터럴의 것을 반영하지 않기 때문입니다.) 이 들여쓰기와 《동등한》 공백이 문자열의 모든 줄의 시작 부분에서 제거됩니다. 덜 들여쓰기 된 줄이 나타나지는 말아야 하지만, 나타난다면 모든 앞부분의 공백이 제거됩니다. 공백의 동등성은 탭 확장 (보통 8개의 스페이스) 후에 검사됩니다.
여기 여러 줄 독스트링의 예가 있습니다:
>>> def my_function():
... """Do nothing, but document it.
...
... No, really, it doesn't do anything.
... """
... pass
...
>>> print(my_function.__doc__)
Do nothing, but document it.
No, really, it doesn't do anything.
4.7.7. 함수 어노테이션¶
함수 어노테이션 은 사용자 정의 함수가 사용하는 형들에 대한 완전히 선택적인 메타데이터 정보입니다 (자세한 내용은 PEP 3107 과 PEP 484 를 보세요).
어노테이션은 함수의 __annotations__
어트리뷰트에 딕셔너리로 저장되고 함수의 다른 부분에는 아무런 영향을 미치지 않습니다. 파라미터 어노테이션은 파라미터 이름 뒤에 오는 콜론으로 정의되는데, 값을 구할 때 어노테이션의 값을 주는 표현식이 뒤따릅니다. 반환 값 어노테이션은 리터럴 ->
와 그 뒤를 따르는 표현식으로 정의되는데, 파라미터 목록과 def
문의 끝을 나타내는 콜론 사이에 놓입니다. 다음 예에서 위치 인자, 키워드 인자, 반환 값이 어노테이트 됩니다:
>>> def f(ham: str, eggs: str = 'eggs') -> str:
... print("Annotations:", f.__annotations__)
... print("Arguments:", ham, eggs)
... return ham + ' and ' + eggs
...
>>> f('spam')
Annotations: {'ham': <class 'str'>, 'return': <class 'str'>, 'eggs': <class 'str'>}
Arguments: spam eggs
'spam and eggs'
4.8. 막간극: 코딩 스타일¶
이제 여러분은 파이썬의 더 길고, 더 복잡한 조각들을 작성하려고 합니다, 코딩 스타일 에 대해 말할 적절한 시간입니다. 대부분 언어는 서로 다른 스타일로 작성될 (또는 더 간략하게, 포맷될) 수 있습니다; 어떤 것들은 다른 것들보다 더 읽기 쉽습니다. 다른 사람들이 여러분의 코드를 읽기 쉽게 만드는 것은 항상 좋은 생각이고, 훌륭한 코딩 스타일을 도입하는 것은 그렇게 하는 데 큰 도움을 줍니다.
파이썬을 위해, 대부분 프로젝트가 고수하는 스타일 가이드로 PEP 8 이 나왔습니다; 이것은 매우 읽기 쉽고 눈이 편안한 코딩 스타일을 장려합니다. 모든 파이썬 개발자는 언젠가는 이 문서를 읽어야 합니다; 여러분을 위해 가장 중요한 부분들을 추려봤습니다:
들려 쓰기에 4-스페이스를 사용하고, 탭을 사용하지 마세요.
4개의 스페이스는 작은 들여쓰기 (더 많은 중첩 도를 허락한다) 와 큰 들여쓰기 (읽기 쉽다) 사이의 좋은 절충입니다. 탭은 혼란을 일으키고, 없애는 것이 최선입니다.
79자를 넘지 않도록 줄 넘김 하세요.
이것은 작은 화면을 가진 사용자를 돕고 큰 화면에서는 여러 코드 파일들을 나란히 볼 수 있게 합니다.
함수, 클래스, 함수 내의 큰 코드 블록 사이에 빈 줄을 넣어 분리하세요.
가능하다면, 주석은 별도의 줄로 넣으세요.
독스트링을 사용하세요.
연산자들 주변과 콤마 뒤에 스페이스를 넣고, 괄호 바로 안쪽에는 스페이스를 넣지 마세요:
a = f(1, 2) + g(3, 4)
.클래스와 함수들에 일관성 있는 이름을 붙이세요; 관례는 클래스의 경우
CamelCase
, 함수와 메서드의 경우lower_case_with_underscores
입니다. 첫 번째 메서드 인자의 이름으로는 항상self
를 사용하세요 (클래스와 메서드에 대한 자세한 내용은 클래스와의 첫 만남 을 보세요).여러분의 코드를 국제적인 환경에서 사용하려고 한다면 특별한 인코딩을 사용하지 마세요. 어떤 경우에도 파이썬의 기본, UTF-8, 또는 단순 ASCII조차, 이 최선입니다.
마찬가지로, 다른 언어를 사용하는 사람이 코드를 읽거나 유지할 약간의 가능성만 있더라도, 식별자에 ASCII 이외의 문자를 사용하지 마세요.
각주
- 1
실제로, 객체 참조에 의한 호출 (call by object reference) 이 더 좋은 표현인데, 가변 객체가 전달되면, 호출자는 피호출자가 만든 변경을 볼 수 있기 때문입니다 (가령 리스트에 항목을 추가합니다).