Introducing Python(처음 시작하는 파이썬)
Chapter 4. 파이 크러스트: 코드 구조
- 파이썬은 프로그램의 구조를 표현하기 위해 중괄호가 아닌, 공백을 사용하는 흔치 않은 언어이다.
- 즉, 파이썬 프로그램에서는 Indentation(들여쓰기)를 통해 Block을 구분짓는다.
4.1 Comment with # (코멘트 달기: #)
- 코멘트(주석)는 인터프리터가 무시하는 텍스트 영역이다.
- 즉, 코멘트는 코드에 특정한 표시를 해두거나, 코드를 설명하는 등 다양한 목적으로 활용된다.
- # 문자가 시작된 곳부터 그 라인의 마지막까지 주석으로 처리된다.
- 문자열 내에 있는 #은 문자열의 일원으로 간주된다.
(즉, 따옴표 사이에 있는 #은 주석 표시가 아니다.)
- # 문자는 "Hash", "Sharp", "Pound", "Octothorpe" 등으로 불린다.
- 파이썬에는 여러 라인을 한 꺼번에 주석으로 처리하는 기능이 없다.
4.2 Continue Lines with \ (라인 유지하기: \)
- 파이썬에서는 한 라인에 권장하는 최대 문자수는 80자이다.
- 한 문장을 80자 이내로 표현할 수 없는 경우,
라인 끝에 백 슬래시(\)를 입력하여 다음에 붙는 라인도 같은 라인임을 명시할 수 있다.
>>> alphabet = 'abcdefg' + \
... 'hijklmnop' + \
... 'qrstuvwx' + \
... 'yz'
>>> print(alphabet)
abcdefghijklmnopqrstuvwxyz
4.3 Compare with if, elif, and else (비교하기: if, elif, else)
조건문 : if, elif, else
if condition:
~~ # if 구문 내부의 내용은 4타 띄우고 시작
elif condition:
~~
else:
~~
- 파이썬에서 if, elif, else는 조건의 True/False 여부를 판단하는 statement(선언문)이다.
- 위 예시에서 elif는 선택 조건이며, if 단독으로 쓰일 수도 있다.
* 파이썬에서의 블럭 구분 : 4칸의 Indentation(들여쓰기)
- 파이썬 코드 권장 스타일(pep-8)에서는 4타수의 들여쓰기를 통해 하위 블럭을 구분짓는 것으로 규정했다.
- 즉 C/C++에서 중괄호로 블럭을 구분하는 것처럼, 파이썬에서는 중괄호 대신 들여쓰기로 구분하는 것이다.
- Tab키와 스페이스를 혼합하여 들여쓰기 하는 것은 권장되지 않는다.
(시스템에서 정의한 Tab 크기에 따라 공백의 수가 흐트러질 수 있다.)
* Python's Comparison Operators (파이썬의 비교 연산자)
비교 연산자 | is | == | != | < | <= | > | >= | in |
의미 | 같다 (레퍼런스) |
같다 (값) |
다르다 | 작다 | 작거나 같다 | 크다 | 크거나 같다 | 시퀀스 멤버쉽 |
※ 파이썬에서는 하나의 변수를 아래와 같이 여러 번 비교하는 것을 허용한다.
5 < x < 10 # x가 5와 10 사이에 들어오면 True 리턴
* Python's Boolean Operators (파이썬의 부울 연산자)
부울 연산자 | and | or | not |
의미 | AND 연산 | OR 연산 | NOT 연산 |
- 동시에 여러 식을 비교해야 할 경우 사용하는 연산자이다.
- 부울 연산자는 비교 연산자보다 우선순위가 낮다.
4.3.1 What Is True? (True와 False)
* 암시적으로, False로 간주되는 것
False로 간주 | null | 정수 0 | 부동소수점수 0 | 빈 문자열 | 빈 리스트 | 빈 튜플 | 빈 딕셔너리 | 빈 셋 |
False = | None | 0 | 0.0 | '' | [] | () | {} | set() |
- 위 요소들 이외에 다른 것들은 모두 True로 간주된다.
4.4 Repeat with while (반복하기: while)
while문
- 조건이 참이면, Loop를 실행한다.
while condition:
~~ # while 구문 내부의 내용은 4타 띄우고 시작
4.4.1 Cancel with break (중단하기: break)
- 현재 속한 Loop에서 탈출하고자 할 때, break문을 사용한다.
while True:
stuff = input("String to capitalize [type q to quit]: ")
if stuff == "q":
break
print(stuff.capitalize())
※ \(\texttt{input()}\), \(\texttt{print()}\)는 Python의 Built-in Function(내장형 함수)이다.
4.4.2 Skip Ahead with continue (건너뛰기: continue)
- Loop의 중단은 원치 않으면서, 해당 루프 주기는 건너뛰고자 할 때 continue문을 사용한다.
while True:
value = input("Integer, please [q to quit]: ")
if value == 'q':
break
number = int(value)
if number % 2 == 0:
continue # continue문은 나머지 코드는 실행시키지 않고, 다음 루프 주기를 실행하게 한다.
print(number, "squared is", number*number)
4.4.3 Check break Use with else (break 확인하기: else)
※ Loop와 연관된 else문은 break Checker의 역할을 한다.
- while, for와 같은 loop가 종료될 때 까지, 루프 안 break 문이 실행되지 않으면, 루프를 뒤따르는 else문이 실행된다.
while position < len(numbers):
if value == 'q':
break
number = int(value)
else:
print('No even number found') # 루프에서 break문이 한 번도 실행되지 않았을 경우, 실행된다.
4.5 Iterate with for (순회하기: for)
- for문을 통해 특정한 Data Structure 또는 Object의 원소들을 하나씩 Iterate(순회)할 수 있다.
- for문 내에서 바로 생성된 객체 또한 for문으로 순회 가능하다.
- for문을 통한 문자열 순회 시, 한 문자씩 순회한다.
- for문을 통한 리스트, 튜플 순회 시, 한 Item씩 순회한다.
- for문을 통한 딕셔너리 순회 시, 한 Key씩 순회한다.
for name in iterable_Structure: # 순회 가능한 객체의 전체를 순회
print(name) # 순회 가능한 객체: 리스트, 튜플, 딕셔너리, 셋, 문자열 등
~~~~
※ while Loop vs for Loop
# while Loop (To step through a sequence)
>>> rabbits = ['Flopsy', 'Mopsy', 'Cottontail', 'Peter']
>>> current = 0
>>> while current < len(rabbits):
... print(rabbits[current])
... current += 1
...
Flopsy
Mopsy
Cottontail
Peter
# for Loop (More pythonic way)
>>> rabbits = ['Flopsy', 'Mopsy', 'Cottontail', 'Peter']
>>> for rabbit in rabbits:
... print(rabbit)
...
Flopsy
Mopsy
Cottontail
Peter
※ for문을 통한 딕셔너리 순회
# for문은 기본적으로 딕셔너리의 Key를 순회한다.
>>> accusation = {'room': 'ballroom', 'weapon': 'lead pipe', 'person': 'Col. Mustard'}
>>> for card in accusation: # 또는, for card in accusation.keys():
... print(card)
...
room
weapon
person
# 딕셔너리의 값을 통해 순회하기 위해선 dict.values() 함수를 사용한다.
>>> accusation = {'room': 'ballroom', 'weapon': 'lead pipe', 'person': 'Col. Mustard'}
>>> for value in accusation.values():
... print(value)
...
ballroom
lead pipe
Col. Mustard
# 딕셔너리의 Key-Value Pair를 모두 리턴하게 하기 위해, dict.items() 함수를 사용한다.
# dict.items() 함수는 딕셔너리의 한 Item을 튜플로 리턴한다.
>>> accusation = {'room': 'ballroom', 'weapon': 'lead pipe', 'person': 'Col. Mustard'}
>>> for item in accusation.items():
... print(item)
...
('room', 'ballroom')
('weapon', 'lead pipe')
('person', 'Col. Mustard')
# 좀 더 유연한 Key, Value 활용 방법
>>> accusation = {'room': 'ballroom', 'weapon': 'lead pipe', 'person': 'Col. Mustard'}
>>> for card, contents in accusation.items():
... print('Card', card, 'has the contents', contents)
...
Card weapon has the contents lead pipe
Card person has the contents Col. Mustard
Card room has the contents ballroom
4.5.1 Cancel with break (중단하기: break)
- 현재 속한 Loop에서 탈출하고자 할 때, break문을 사용한다.
(while문에서의 break문과 동일한 동작을 수행한다.)
4.5.2 Skip with continue (건너뛰기: continue)
- Loop의 중단은 원치 않으면서, 해당 루프 주기는 건너뛰고자 할 때 continue문을 사용한다.
(while문에서의 continue문과 동일한 동작을 수행한다.)
4.5.3 Check break Use with else (break 확인하기: else)
※ Loop와 연관된 else문은 break Checker의 역할을 한다.
- while, for와 같은 loop가 종료될 때 까지, 루프 안 break 문이 실행되지 않으면, 루프를 뒤따르는 else문이 실행된다.
cheeses = []
for cheese in cheeses:
print('This shop has some lovely', cheese)
break
else: # no break means no cheese
print('This is not much of a cheese shop, is it?')
* else 대신, else의 기능을 하는 변수를 사용하여 표현할 수 있다.
cheeses = []
found_one = False
for cheese in cheeses:
found_one = True
print('This shop has some lovely', cheese)
break
if not found_one:
print('This is not much of a cheese shop, is it?')
4.5.4Iterate Multiple Sequences with zip() (여러 시퀀스 순회하기: zip())
- zip()을 이용하여 여러 시퀀스를 병렬로 순회할 수 있다.
- 여러 시퀀스를 병렬로 순회하고, 그 결과를iterable한 시퀀스로 리턴한다.
- 여러 시퀀스를 병렬로 순회하면서, 가장 짧은 시퀀스의 순회가 완료되면, zip()의 순회도 종료된다.
>>> days = ['Monday', 'Tuesday', 'Wednesday']
>>> fruits = ['banana', 'orange', 'peach']
>>> drinks = ['coffee', 'tea', 'beer']
>>> desserts = ['tiramisu', 'ice cream', 'pie', 'pudding']
>>> for day, fruit, drink, dessert in zip(days, fruits, drinks, desserts):
... print(day, ": drink", drink, "- eat", fruit, "- enjoy", dessert)
...
Monday : drink coffee - eat banana - enjoy tiramisu
Tuesday : drink tea - eat orange - enjoy ice cream
Wednesday : drink beer - eat peach - enjoy pie
* zip()을 이용하여, 다수의 시퀀스를 합칠 수 있다.
>>> english = 'Monday', 'Tuesday', 'Wednesday'
>>> french = 'Lundi', 'Mardi', 'Mercredi'
>>> list( zip(english, french) )
[('Monday', 'Lundi'), ('Tuesday', 'Mardi'), ('Wednesday', 'Mercredi')]
>>> dict( zip(english, french) )
{'Monday': 'Lundi', 'Tuesday': 'Mardi', 'Wednesday': 'Mercredi'}
4.5.5 Generate Number Sequences with range() (숫자 시퀀스 생성하기: range())
* range(start, stop, step)
- iterable한 숫자 시퀀스를 리턴한다.
- start에서 (stop - 1) 까지의 숫자 시퀀스를 리턴한다.
- step은 스텝 수를 의미하며, 음수도 가능하다. 기본값은 1이다.
- start의 기본값은 0이며, stop은 필수적으로 입력해야 한다.
- 제너레이터 중 하나이다.
- Python 2에서의 range() 함수는 메모리에 제한적인 리스트를 리턴한다.
- Python 2의 xrange() 함수가 Python 3의 range() 함수로 계승되었다.
* range() 활용하여 숫자들로 구성된, 다양한 시퀀스를 만들어낼 수 있다.
>>> for x in range(0,3):
... print(x)
...
0
1
2
>>> list( range(0, 3) )
[0, 1, 2]
>>> for x in range(2, -1, -1):
... print(x)
...
2
1
0
>>> list( range(2, -1, -1) )
[2, 1, 0]
>>> list( range(0, 11, 2) )
[0, 2, 4, 6, 8, 10]
>>> sum( range(1, 101) )
5050
4.5.6 Other Iterators (기타 이터레이터)
4.6 Comprehensions (컴프리헨션, 함축)
- Comprehension을 이용하여 하나 이상의 Iterator로부터 파이썬 자료 구조를 만들 수 있다.
- 컴프리헨션은 if 테스트와 다중 for 절을 가질 수 있다.
- 컴프리헨션 Syntax 자체가 굉장히 함축적이고 간결해서 컴프리헨션 Syntax 그 자체로 Pythonic 면을 보인다.
※ 리스트, 딕셔너리, 셋 컴프리헨션을 구현할 수 있으며, 튜플은 컴프리헨션 표현이 정의되어 있지 않다.
- 소괄호를 이용한 컴프리헨션 표현은 튜플 컴프리헨션이 아닌, Generator Comprehension이다.
4.6.1 List Comprehension (리스트 컴프리헨션)
# List Comprehension을 이용하여 정수 리스트를 만드는 방법 (More pythonic way)
# Way 1
[표현식 for 항목 in 순회 가능한 객체]
>>> number_list = [number for number in range(1,6)]
>>> number_list
[1, 2, 3, 4, 5]
>>> number_list = [number-1 for number in range(1,6)]
>>> number_list
[0, 1, 2, 3, 4]
# Way 2
[표현식 for 항목 in 순회 가능한 객체 if 조건] # if 테스트 컴프리헨션
>>> a_list = [number for number in range(1,6) if number % 2 == 1]
>>> a_list
[1, 3, 5]
# Way 3 (다중 for 루프 컴프리헨션)
# Way 3에서도, Way 2와 같이, 각 단계의 for문 끝에 조건식을 작성할 수 있다.
[표현식 for 항목_1 in 순회 가능한 객체_1 for 항목_2 in 순회 가능한 객체_2]
>>> rows = range(1,4)
>>> cols = range(1,3)
>>> cells = [(row, col) for row in rows for col in cols]
>>> for cell in cells:
... print(cell)
...
(1, 1)
(1, 2)
(2, 1)
(2, 2)
(3, 1)
(3, 2)
4.6.2 Dictionary Comprehension (딕셔너리 컴프리헨션)
# Deictionary Comprehension
{키_표현식 : 값_표현식 for 표현식 in 순회 가능한 객체}
{키_표현식 : 값_표현식 for 항목 in 순회 가능한 객체 if 조건}
{키_표현식 : 값_표현식 for 항목_1 in 순회 가능한 객체_1 for 항목_2 in 순회 가능한 객체_2}
>>> word = 'letters'
>>> letter_counts = {letter: word.count(letter) for letter in word}
>>> letter_counts
{'l': 1, 'e': 2, 't': 2, 'r': 1, 's': 1}
>>> word = 'letters'
>>> letter_counts = {letter: word.count(letter) for letter in set(word)}
>>> letter_counts
{'t': 2, 'l': 1, 'e': 2, 'r': 1, 's': 1}
4.6.3 Set Comprehension (셋 컴프리헨션)
# Set Comprehension (딕셔너리에서 값만 제거된 형태)
{키_표현식 for 표현식 in 순회 가능한 객체}
{키_표현식 for 항목 in 순회 가능한 객체 if 조건}
{키_표현식 for 항목_1 in 순회 가능한 객체_1 for 항목_2 in 순회 가능한 객체_2}
>>> a_set = {number for number in range(1,6) if number % 3 == 1}
>>> a_set
{1, 4}
※ 중괄호 내에 원소가 한 쌍씩 들어있으면 딕셔너리, 하나씩 들어있으면 셋 임에 유의하자.
4.6.4 Generator Comprehension (제너레이터 컴프리헨션)
- 소괄호를 이용한 Comprehension은 튜플을 생성하는 것이 아닌, Generator 객체를 생성한다.
- Generator는 Iterator에 데이터를 제공하는 하나의 방법이다.
- Generator는 한 번만 실행될 수 있다.
- 리스트, 셋, 딕셔너리, 문자열은 메모리에 존재하지만, Generator는 즉석에서 값을 생성하고,
Iterator를 통해 한 번에 값을 하나씩 처리한다.
- Generator는 이 값을 기억하지 않으므로 다시 시작하거나 Generator를 백업할 수 없다.
# Generator Comprehension (튜플 컴프리헨션은 존재하지 않는다!)
(표현식 for 항목 in 순회 가능한 객체)
(표현식 for 항목 in 순회 가능한 객체 if 조건)
(표현식 for 항목_1 in 순회 가능한 객체_1 for 항목_2 in 순회 가능한 객체_2)
>>> number_thing = (number for number in range(1, 6))
>>> type(number_thing)
<class 'generator'>
>>> for number in number_thing:
... print(number)
...
1
2
3
4
5
>>> for number in number_thing:
... print(number)
...
>>>
# Generator는 한 번만 순회할 수 있다.
* Generator는 Comprehension을 통해 생성될 수 있고, Generator Function을 통해 생성될 수도 있다.
4.7 Functions (함수)
- 함수 정의는 def 키워드, 함수 이름, 소괄호, 콜론으로 구성된다.
- 함수 이름 명명 규칙은 변수 명명 규칙과 동일하다.
(영문 대소문자, 숫자, 언더스코어로 구성되며, 맨 첫 글자는 숫자일 수 없다.)
- return 문으로 값을 반환한다.
- 함수의 Argument(인자)는 개수에 상관없이 모든 타입을 인자로 취할 수 있다.
- 리턴값도 인자와 마찬가지로, 개수에 상관없이 모든 타입을 반환할 수 있다.
- 함수 정의에서 명시적으로 return문을 호출하지 않으면, 함수는 None을 반환한다.
* 아무 작업도 하지않는 do_nothing() 함수 정의
def do_nothing():
pass
# pass문은 아무 작업도 시행하지 않는다.
※ Special Value: None (Immutable)
- None은 "아무것도 없다"를 뜻하는 특별한 값이다.
- None이 Boolean 타입으로 사용될 땐, False값처럼 사용된다.
- 하지만 근본적으로, None과 False 사이에는 차이가 있다.
- False와 None 사이를 구분짓기 위해 is 연산자를 사용할 것이 권장된다.
# None이 False처럼 사용되는 경우
>>> thing = None
>>> if thing:
... print("It's some thing")
... else:
... print("It's no thing")
...
It's no thing
# is 연산자를 통해 가독성을 개선한 경우
>>> if thing is None:
... print("It's nothing")
... else:
... print("It's something")
...
It's nothing
# False와 None의 차이점
>>> def is_none(thing):
... if thing is None:
... print("It's None")
... elif thing:
... print("It's True")
... else:
... print("It's False")
...
>>> is_none(None)
It's None
>>> is_none(True)
It's True
>>> is_none(False)
It's False
>>> is_none(0)
It's False
>>> is_none(0.0)
It's False
>>> is_none(())
It's False
>>> is_none([])
It's False
>>> is_none({})
It's False
>>> is_none(set())
It's False
4.7.1 Positional Arguments (위치 인자)
- 값을 순서대로 상응하는 매개변수에 복사하는 방식이다.
- 각 매개변수의 위치를 파악하고 있어야 사용이 가능하다.
>>> def menu(wine, entree, dessert):
... return {'wine': wine, 'entree': entree, 'dessert': dessert}
...
>>> menu('chardonnay', 'chicken', 'cake')
{'dessert': 'cake', 'wine': 'chardonnay', 'entree': 'chicken'}
# 각각의 Argument들이 순서대로 Parameter에 대응된다.
# 'chardonnay'는 wine에 대응된다.
# 'chicken'은 entree에 대응된다.
# 'cake'는 dessert에 대등된다.
4.7.2 Keyword Arguments (키워드 인자)
- 함수 호출부에서 소괄호 안에, 매개변수 이름과 인자를 = 연산자로 직접 엮어서 표현하는 방식이다.
- 이렇게 매개변수에 인자를 명시할 경우, 매개변수의 원래 순서는 의미가 없어진다.
- 위치 인자와 키워드 인자를 섞어 사용할 수 있다. 단, 위치 인자가 먼저 와야 한다.
>>> def menu(wine, entree, dessert):
... return {'wine': wine, 'entree': entree, 'dessert': dessert}
...
# 전체 Argument를 모두 일일히 지정
>>> menu(entree='beef', dessert='bagel', wine='bordeaux')
{'dessert': 'bagel', 'wine': 'bordeaux', 'entree': 'beef'}
# 일부 Argument만 지정 -> 반드시 Positional Argiments는 앞에 위치해야 한다.
>>> menu('frontenac', dessert='flan', entree='fish')
{'entree': 'fish', 'dessert': 'flan', 'wine': 'frontenac'}
4.7.3 Specify Default Parameter Values (기본 매개변수값 저장하기)
- Parameter에 기본값이 지정된 상태에서 Argument가 전달되지 않으면, Parameter에 지정한 기본값이 배정된다.
- 기본 인자값은 반드시 매개변수 리스트의 오른쪽부터 지정해 나아가야 한다.
>>> def menu(wine, entree, dessert='pudding'):
... return {'wine': wine, 'entree': entree, 'dessert': dessert}
>>> menu('chardonnay', 'chicken')
{'dessert': 'pudding', 'wine': 'chardonnay', 'entree': 'chicken'}
※ 기본 인자값은 함수가 실행될 때 계산되지 않고, 함수를 정의할 때 한 번만 계산된다.
- 즉, 컴파일될 때 한 번만 배정된다.
- Mutable한 객체(리스트, 딕셔너리 등)를 기본 인자로 설정하면,
최초로 호출될 경우에만 해당 기본 인자값으로 초기화되고,
그 이후 호출부에선 이전 호출에서 초기화 된 내용으로 유지하게 된다.
>>> def buggy(arg, result=[]):
... result.append(arg)
... print(result)
...
>>> buggy('a')
['a']
>>> buggy('b') # expect ['b']
['a', 'b']
>>> def nonbuggy(arg, result=None): # None은 Immutable한 객체이므로, 한 번 result에 None이 배정된 이상, 변하지 않는다.
... if result is None:
... result = []
... result.append(arg)
... print(result)
...
>>> nonbuggy('a')
['a']
>>> nonbuggy('b')
['b']
4.7.4 Gather Positional Arguments with * (위치 인자 모으기: *)
- 함수의 매개변수에 애스터리스크(*)를 사용하면, *는 매개변수에서 위치 인자 변수들을 튜플로 묶는 역할을 한다.
- 가변 인자명은 관용적으로 *args 로 표현한다.
>>> def print_args(*args):
... print('Positional argument tuple:', args)
...
>>> print_args()
Positional argument tuple: ()
>>> print_args(3, 2, 1, 'wait!', 'uh...')
Positional argument tuple: (3, 2, 1, 'wait!', 'uh...')
* 보다 유용한 *args 활용법
>>> def print_more(required1, required2, *args):
... print('Need this one:', required1)
... print('Need this one too:', required2)
... print('All the rest:', args)
...
>>> print_more('cap', 'gloves', 'scarf', 'monocle', 'mustache wax')
Need this one: cap
Need this one too: gloves
All the rest: ('scarf', 'monocle', 'mustache wax')
4.7.5 Gather Keyword Arguments with ** (키워드 인자 모으기: **)
- 함수의 매개변수에 키워드 인자들을 딕셔너리로 묶기 위해 **을 사용한다.
- 인자의 이름은 키고, 값은 이 키에 대응하는 딕셔너리 값이다.
- 가변 인자명은 관용적으로 **kwargs 로 표현한다.
>>> def print_kwargs(**kwargs):
... print('Keyword arguments:', kwargs)
...
>>> print_kwargs(wine='merlot', entree='mutton', dessert='macaroon')
Keyword arguments: {'dessert': 'macaroon', 'wine': 'merlot', 'entree': 'mutton'}
※ *args와 **kwargs를 섞어서 사용하려면, 이들을 순서대로 배치해야 한다.
4.7.6 Docstrings (docstring)
- 함수 몸체 시작 부분에 docstring 문자열을 포함시켜 함수 정의에 Documentation(문서)를 붙일 수 있다.
- docstring은 길게 작성할 수 있고, Formatting(서식)을 추가할 수도 있다.
def echo(anything):
'echo returns its input argument' # docstring
return anything
>>> help(echo)
Help on function echo in module __main__:
help(함수명)
# 해당 함수의 매개변수 정보와, 서식화된 docstring을 출력한다. 함수 이름을 매개변수로 한다.
help(함수명.__doc__)
# 서식 없는 docstring을 출력한다.
* docstring 작성과 help() 함수를 통한 docstring 출력이 가능하다.
def print_if_true(thing, check):
'''
Prints the first argument if a second argument is true.
The operation is:
1. Check whether the *second* argument is true.
2. If it is, print the *first* argument.
'''
if check:
print(thing)
>>> help(print_if_true)
Help on function print_if_true in module __main__:
print_if_true(thing, check)
Prints the first argument if a second argument is true.
The operation is:
1. Check whether the *second* argument is true.
2. If it is, print the *first* argument.
>>> help(print_if_true.__doc__)
No Python documentation found for 'Prints the first argument if a second argument is true.\n The operation is:\n 1. Check whether the *second* argument is true.\n 2. If it is, print the *first* argument.'.
Use help() to get the interactive help utility.
Use help(str) for help on the str class.
4.7.7 Functions Are First-Class Citizens (일등 시민: 함수)
- 함수는 Immutable하다.
- 함수는 변수에 할당될 수 있다.
- 함수는 Argument로써 사용 가능하다.
- 함수에서 함수를 리턴할 수 있다.
- 함수를 리스트, 튜플, 딕셔너리, 셋의 요소로 포함시킬 수 있다.
* 파이썬에서 First-Class Citizen과 같은 "함수"
>>> def answer():
... print(42)
>>> answer()
42
>>> def run_something(func):
... func()
>>> run_something(answer)
42
>>> type(run_something)
<class 'function'>
>>> def sum_args(*args):
... return sum(args)
>>> def run_with_positional_args(func, *args):
... return func(*args)
>>> run_with_positional_args(sum_args, 1, 2, 3, 4)
10
* sum() 함수
- Iterable한 숫자(정수, 부동소수점수) 인자의 값을 모두 더하는 Python's Built-In Function이다.
4.7.8 Inner Functions (내부 함수)
- 파이썬에서는 함수 안에 또 다른 함수를 정의할 수 있다.
- 내부 함수는 함수 내에서 복잡한 작업을 한 번 이상 반복할 때, 유용하게 사용된다.
>>> def knights(saying):
... def inner(quote):
... return "We are the knights who say: '%s'" % quote
... return inner(saying)
...
>>> knights('Ni!')
"We are the knights who say: 'Ni!'"
4.7.9 Closures (클로져)
- 클로져는 바깥 함수로부터 생성된 변수값을 변경하고 저장할 수 있는 함수이다.
- 클로져는 다른 함수에 의해 동적으로 생성되는 내부 함수의 일종이다.
>>> def knights2(saying):
... def inner2(): # inner2() 클로져는 외부 함수의 변수를 직접 사용한다.
... return "We are the knights who say: '%s'" % saying
... return inner2
...
>>> a = knights2('Duck')
>>> b = knights2('Hasenpfeffer')
>>> type(a)
<class 'function'>
>>> type(b)
<class 'function'>
>>> a
<function knights2.<locals>.inner2 at 0x10193e158>
>>> b
<function knights2.<locals>.inner2 at 0x10193e1e0>
>>> a()
"We are the knights who say: 'Duck'"
>>> b()
"We are the knights who say: 'Hasenpfeffer'"
4.7.10 Anoymous Functions: the lambda() Function (익명 함수: lambda())
- 파이썬의 람다 함수는 단일문으로 표현되는 Anonymous Function(익명 함수)이다.
- 람다 함수는 1회용의 소형 함수이다.
- 람다 함수는 작은 함수 다수를 정의하고, 이들을 호출하여 얻은 결과값들을 저장해야 하는 경우에 유용하다.
- 람다 함수는 Call Back Functions(콜백 함수)를 정의하는 GUI에 사용할 수 있다.
- 람다의 콜론(:)과 닫는 괄호 사이에 람다 함수의 정의를 기술한다.
* 함수의 Parameter로써 람다 함수를 활용할 수 있다.
# 전통적인 방법
>>> def edit_story(words, func):
... for word in words:
... print(func(word))
>>> stairs = ['thud', 'meow', 'thud', 'hiss']
>>> def enliven(word): # give that prose more punch
... return word.capitalize() + '!'
>>> edit_story(stairs, enliven) # 이전에 따로 정의한 함수를 매개변수로 이용한다.
Thud!
Meow!
Thud!
Hiss!
# 람다 함수를 이용한 방법
>>> edit_story(stairs, lambda word: word.capitalize() + '!') # 매개변수 부분에 람다 함수를 정의한다.
Thud!
Meow!
Thud!
Hiss!
4.8 Generator (제너레이터)
- 파이썬의 제너레이터는 시퀀스를 생성하는 객체이다.
- 제너레이터를 이용해 전체 시퀀스를 한 번에 메모리에 생성하고 정렬할 필요 없이, 아주 큰 시퀀스를 순회할 수 있다.
- 제너레이터는 Iterator에 대한 데이터의 소스로 유용하게 사용된다.
- 제너레이터는 순회할 때마다 마지막으로 호출된 Item을 기억하고, 다음 값을 리턴한다는 점에서 일반 함수와 차이가 있다.
(일반 함수는 이전 호출에 대한 정보가 없고, 항상 동일하게 첫 번째 구문부터 실행한다.)
* range(start, stop, step) 함수 또한, start부터 stop - 1 까지의 숫자 시퀀스를 생성하는 제너레이터이다.
* enumerate(sequence, [start_index = 0])
- iterable한 sequence의 인덱스 값을 Key로,
sequence의 값을 Value로 하는 딕셔너리를 리턴한다.
- 선택사항인 start_index 값을 지정하면, 지정한 인덱스의 원소부터 리턴한다.
* Generator Function (제너레이터 함수)
- 제너레이터 컴프리헨션을 작성하는데 코드의 길이가 길어진다면, 제너레이터 함수를 사용할 것이 권장된다.
- 제너레이터 함수는 return문 대신, yield문으로 값을 리턴한다.
>>> def my_range(first=0, last=10, step=1):
... number = first
... while number < last:
... yield number # 제너레이터에서는 return문 대신, yield문을 사용한다.
... number += step
...
# 제너레이터는 기본적으로 함수의 범주에 속한다.
>>> my_range
<function my_range at 0x10193e268>
# 제너레이터 my_range는 제너레이터 객체를 리턴한다.
>>> ranger = my_range(1, 5)
>>> ranger
<generator object my_range at 0x101a0a168>
# 이 제너레이터 객체를 for문을 통해 순회할 수 있다.
>>> for x in ranger:
... print(x)
...
1
2
3
4
4.9 Decorator (데커레이터)
- 데커레이터는 하나의 함수를 취해서 또 다른 함수를 리턴하는 함수이다.
- 데커레이터는 *args, **kwargs, 내부 함수, 함수 인자 개념을 사용한다.
* 데커레이터 "document_it(func) 함수"의 수동 적용법과 자동 적용법
# 데커레이터 document_it(func) 정의
>>> def document_it(func):
... def new_function(*args, **kwargs):
... print('Running function:', func.__name__)
... print('Positional arguments:', args)
... print('Keyword arguments:', kwargs)
... result = func(*args, **kwargs)
... print('Result:', result)
... return result
... return new_function
>>> def add_ints(a, b):
... return a + b
...
>>> add_ints(3, 5)
8
# 데커레이터 수동 적용
>>> cooler_add_ints = document_it(add_ints)
>>> cooler_add_ints(3, 5)
Running function: add_ints
Positional arguments: (3, 5)
Keyword arguments: {}
Result: 8
8
# 데커레이터 자동 적용
# 수동으로 데커레이터를 할당하는 대신,
# 데커레이터를 사용하고자 하는 함수의 문두에
# "@데커레이터이름" 을 추가한다.
>>> @document_it
... def add_ints(a, b):
... return a + b
...
>>> add_ints(3, 5)
Start function add_ints
Positional arguments: (3, 5)
Keyword arguments: {}
Result: 8
8
* 다수의 데커레이터를 가진 함수 add_ints()
※ 함수의 정의에서 가장 가까운 데커레이터가 먼저 실행된다.
# 데커레이터 document_it(func) 정의
>>> def document_it(func):
... def new_function(*args, **kwargs):
... print('Running function:', func.__name__)
... print('Positional arguments:', args)
... print('Keyword arguments:', kwargs)
... result = func(*args, **kwargs)
... print('Result:', result)
... return result
... return new_function
# 데커레이터 square_it(func) 정의
>>> def square_it(func):
... def new_function(*args, **kwargs):
... result = func(*args, **kwargs)
... return result * result
... return new_function
...
# 두 개의 데커레이터를 적용시킨 add_ints() 함수
>>> @document_it
... @square_it # square_it 데커레이터가 함수 정의에 가장 가까우므로, 먼저 적용된다.
... def add_ints(a, b):
... return a + b
...
# add_ints() 함수 호출 결과
>>> add_ints(3, 5)
Running function: new_function
Positional arguments: (3, 5)
Keyword arguments: {}
Result: 64
64
# 데커레이터를 적용하는 순서에 따른 다른 출력결과
>>> @square_it
... @document_it # document_it 데커레이터가 함수 정의에 가장 가까우므로, 먼저 적용된다.
... def add_ints(a, b):
... return a + b
...
>>> add_ints(3, 5)
Running function: add_ints
Positional arguments: (3, 5)
Keyword arguments: {}
Result: 8
64
4.10 Namespaces and Scope (네임스페이스와 스코프)
- 네임스페이스는 특정 이름이 유일하고, 다른 네임스페이스에서의 동일한 이름과 다름을 정의하는데 이용되는 개념이다.
- 파이썬에서 각 함수들은 자신만의 네임스페이스를 정의한다.
- 메인 프로그램은 Global Namespace(전역 네임스페이스)를 정의한다.
* 함수에서는 전역 변수의 값을 수정할 수 없다.
# 메인 프로그램에서 정의된 전역 변수 animal
>>> animal = 'fruitbat' # Global Namespace에서 정의된 변수 animal
>>> def print_global():
... print('inside print_global:', animal)
...
>>> print('at the top level:', animal)
at the top level: fruitbat
>>> print_global()
inside print_global: fruitbat
# 함수에서 전역 변수의 값을 수정할 수 없다.
>>> def change_and_print_global():
... print('inside change_and_print_global:', animal)
... animal = 'wombat'
... print('after the change:', animal)
...
>>> change_and_print_global()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in change_and_report_it
UnboundLocalError: local variable 'animal' referenced before assignment
# 함수 내에서 같은 이름의 지역 변수를 정의할 수 있다.
>>> def change_local():
... animal = 'wombat' # Local Namespace에서 정의된 변수 animal
... print('inside change_local:', animal, id(animal))
...
>>> change_local()
inside change_local: wombat 4330406160
>>> animal
'fruitbat'
>>> id(animal) # Python Built-In Function "id()"
4330390832
* global 키워드를 통해, Local Namespace에서 Global Namespace의 이름에 접근할 수 있다.
>>> animal = 'fruitbat'
>>> def change_and_print_global():
... global animal
... animal = 'wombat'
... print('inside change_and_print_global:', animal)
...
>>> animal
'fruitbat'
>>> change_and_print_global()
inside change_and_print_global: wombat
>>> animal
'wombat'
* 네임 스페이스의 내용에 접근하는 함수
1. locals() 함수
- 로컬 네임스페이스의 내용이 담긴 딕셔너리를 리턴한다.
2. globals() 함수
- 글로벌 네임스페이스의 내용이 담긴 딕셔너리를 리턴한다.
- 변수명, 값 이외의 다른 부수적인 정보들도 같이 리턴한다.
※ 여기서, 딕셔너리의 Key는 변수이름이고, Value는 변수의 값이다.
# 전역 변수 animal에 할당된 값 'fruitbat'
>>> animal = 'fruitbat'
>>> def change_local():
... animal = 'wombat' # local variable
... print('locals:', locals())
...
# 전역 변수가 출력된다.
>>> animal
'fruitbat'
# 함수의 의도에 따라, 지역 변수가 출력된다.
>>> change_local()
locals: {'animal': 'wombat'}
# Global Namespace에는 변수명, 값 이외에 다른 부수적인 정보들도 포함되어 있다.
# globals() 함수는 그 부수적인 정보들까지 리턴한다.
>>> print('globals:', globals()) # reformatted a little for presentation
globals: {'animal': 'fruitbat',
'__doc__': None,
'change_local': <function change_it at 0x1006c0170>,
'__package__': None,
'__name__': '__main__',
'__loader__': <class '_frozen_importlib.BuiltinImporter'>,
'__builtins__': <module 'builtins'>}
>>> animal
'fruitbat'
4.10.1 Uses of _ and __ in Names (이름에 _와 __사용)
- 파이썬의 예약어들은 두 개의 언더스코어(__)로 시작하고 끝나는 형식을 갖는다.
- 예약어들과의 충돌을 피하기 위해, 일반 변수를 정의할 때에는 위와 같은 형태로 선언하면 안된다.
* function.__name__
- 함수 "function"의 이름이 문자열로 저장되어 있는 System Variable(시스템 변수)이다.
* function.__doc__
- 함수 "function"의 docstring이 저장되어 있는 시스템 변수이다.
4.11 Handle Errors with try and except (에러 처리하기: try, except)
- 어떤 함수에서 Exception(예외)이 발생되었는데, 그곳에서 잡히지 않았다면,
호출한 함수에서라도 일치하는 Handler(핸들러)를 제공하여 해당 예외를 잡을 때까지 Bubbling(버블링)해야 한다.
- 프로그래머가 예외 처리를 위한 핸들러를 따로 제공하지 않으면,
파이썬은 에러 메시지와 오류가 발생한 위치에 대한 정보를 출력하고 프로그램을 강제로 종료시킨다.
* try문
- 예외 발생의 가능성이 있는 코드를 포함시켜 감시하는 Block이다.
- try Block 내에서 예외가 발생되면, except Block이 실행된다.
- try Block 내의 구문이 모두 실행되는 동안 예외가 발생되지 않으면, except Block을 건너뛴다.
* except문
- 발생된 예외를 실질적으로 처리하는 Block이다.
- Argument가 없는 except문은 모든 예외 유형을 처리하는 Block임을 의미한다.
(모든 유형에 대응해야 하니, 포괄적이며, 세심하지 못할 수 있다.)
- 두 개 이상의 예외 타입이 발생될 것이 예상된다면, 각각 별도의 핸들러를 제공하는 것이 바람직하다.
>>> short_list = [1, 2, 3]
>>> position = 5
>>> try:
... short_list[position]
... except:
... print('Need a position between 0 and', len(short_list)-1, ' but got', position)
...
Need a position between 0 and 2 but got 5
* except 예외타입 as 이름
- 변수 이름을 통해 예외사항에 대한 세부정를 알 수 있다.
>>> short_list = [1, 2, 3]
>>> while True:
... value = input('Position [q to quit]? ')
... if value == 'q':
... break
... try:
... position = int(value)
... print(short_list[position])
... except IndexError as err: # 변수 err에 IndexError 예외가 저장된다.
... print('Bad index:', position)
... except Exception as other: # 변수 other에 IndexError를 제외한 기타 예외(Exception)이 저장된다.
... print('Something else broke:', other)
...
Position [q to quit]? 1
2
Position [q to quit]? 0
1
Position [q to quit]? 2
3
Position [q to quit]? 3
Bad index: 3
Position [q to quit]? 2
3
Position [q to quit]? two
Something else broke: invalid literal for int() with base 10: 'two'
Position [q to quit]? q
4.12 Make Your Own Exceptions (예외 만들기)
- 파이썬 표준 라이브러리에는 종류별 예외들이 미리 정의되어 있다. (IndexError 등)
- 프로그래머는 예외 타입을 스스로 정의할 수 있다.
- 예외는 기본적으로 하나의 클래스 객체이며, 예외를 만들기 위해서는 클래스 객체 타입을 정의해야 한다.
- 모든 예외 클래스는 Exception 클래스의 자식 클래스이다.
* words 문자열에 대문자가 발견되면 발생되는 UppercaseException 예외 정의
>>> class UppercaseException(Exception):
... pass # 아무런 동작도 하지 않는 pass문
...
>>> words = ['eeenie', 'meenie', 'miny', 'MO']
>>> for word in words:
... if word.isupper():
... raise UppercaseException(word)
...
Traceback (most recent call last):
File "<stdin>", line 3, in <module>
__main__.UppercaseException: MO
# 예외 객체에 접근하여 내용을 출력
>>> try:
... raise OopsException('panic')
... except OopsException as exc:
... print(exc)
...
panic
* raise문
- 사용자가 직접 예외를 발생시킬 때 사용한다.
4.13 Things to Do (연습문제)
4.1 guess_me 변수에 7을 할당한다.
그리고 값이 7보다 작으면 문자열 'too low'를,
7보다 크면 'too high'를,
7과 같으면 'just right'를 출력하는 조건 테스트(if, elif, else)를 작성하라.
Sol 4.1
guess_me = 7
if guess_me < 7:
print('too low')
elif guess_me > 7:
print('too high')
else:
print('just right')
4.2 guess_me 변수에 7을 할당하고, start 변수에 1을 할당한다.
start와 guess_me를 비교하는 while 문을 작성하라.
start가 guess_me보다 작은 경우에는 'too low'를 출력한다.
start와 guess_me가 같은 경우에는 'found it!'을 출력하고 루프를 종료한다.
start가 guess_me보다 큰 경우에는 'oops'를 출력하고 루프를 종료한다.
그리고 루프의 마지막에 start를 1씩 증가시킨다.
Sol 4.2
guess_me = 7
start = 1
while True:
if start < guess_me:
print('too low')
elif start == guess_me:
print('found it!')
break
else:
print('oops')
break
start += 1
4.3 for 문으로 리스트값 [3, 2, 1, 0]을 출력하라.
Sol 4.3
for item in [3, 2, 1, 0]:
print(item)
4.4 리스트 컴프리헨션을 이용하여 range(10) 에서 짝수 리스트를 만들어라.
Sol 4.4
even_list = [item for item in range(10) if item % 2 == 0]
4.5 딕셔너리 컴프리헨션을 이용하여 squares 딕셔너리를 생성하라.
range(10) 의 반환값을 키로 하고, 각 키의 제곱을 값으로 한다.
Sol 4.5
squares = {item:item*item for item in range(10)}
4.6 셋 컴프리헨션을 이용하여 range(10) 에서 홀수 셋을 만들어라.
Sol 4.6
odd_set = {item for item in range(10) if item % 2 == 1}
4.7 제너레이터 컴프리헨션을 이용하여 문자열 'Got ' 과 range(10)의 각 숫자를 반환하라.
for문을 사용해서 제너레이터를 순회한다.
Sol 4.7
for items in ('Got %s' % value for value in range(10)):
print(items)
4.8 ['Harry', 'Ron', 'Hermione'] 리스트를 반환하는 good 함수를 정의하라.
Sol 4.8
def good():
return ['Harry', 'Ron', 'Hermione']
4.9 range(10)의 홀수를 반환하는 get_odds 제너레이터 함수를 정의하라.
for 문으로 반환된 세 번째 홀수를 찾아서 출력하라.
Sol 4.9
def get_odds():
for value in range(0, 10, 2):
yield value
for count, value in enumerate(get_odds()):
if count == 2: # count는 0부터 1씩 증가된다.
print(value)
break
4.10 어떤 함수가 호출되면 'start'를,
끝나면 'end'를 출력하는 test 데커레이터를 정의하라.
Sol 4.10
def test(func):
def welcome_function(*args, **kwargs):
print('start')
result = func(*args, **kwargs)
print('end')
return result
return welcome_function
@test
def example():
print('Test about it')
example()
4.11 OopsException이라는 예외를 정의하라.
이 예외를 발생시켜서 무슨 일이 일어나는지 살펴보라.
이 예외를 잡아서 'Caught an oops'를 출력하는 코드를 작성하라.
Sol 4.11
class OopsException(Exception):
pass
try:
raise OopsException()
except OopsException:
print('Caught an oops')
4.12 zip() 함수를 사용하여 다음 두 리스트를 짝으로 하는 movies 딕셔너리를 만들어라.
titles = ['Creature of Habit', 'Crewel Fate']
plots = ['A nun turns into a mon ster', 'A haunted yarn shop']
Sol 4.12
titles = ['Creature of Habit', 'Crewel Fate']
plots = ['A nun turns into a mon ster', 'A haunted yarn shop']
movies = dict(zip(titles, plots))
Reference: Introducing Python(처음 시작하는 파이썬) (Bill Lubanovic 저, O'Reilly, 2015)