함수
함수를 사용하는 이유
- 코드 재사용
- 프로그램 모듈화
- 가독성
- 유지보수
ls1 = [1,2,3,4,5]
ls2 = [10,20,30,40,50]
r1 = 0
for v in ls1:
r1 += v
r2 = 0
for v in ls2:
r2 += v
print(r1, r2)
함수를 위한 두가지 작업
- 정의
- 호출
ls1 = [1, 2, 3, 4, 5]
ls2 = [10, 20, 30, 40, 50]
r1 = sum(ls1)
r2 = sum(ls2)
print(r1, r2)
함수의 구조
함수 호출 시 코드의 흐름과 Call Stack
함수의 종료 조건
- 더 이상 실행할 코드가 없을 때
- return 문을 실행했을 때
아무것도 return을 하지 않고 종료하는 함수
- None을 리턴하면서 종료
def do ():
print('이것은 아무 것도 리턴하지 않고 종료되므로 None 리턴')
result = do()
print(result)
#결과
이것은 아무 것도 리턴하지 않고 종료되므로 None 리턴
None
None
- 아무것도 없다라는 의미의 특별한 값
- 부울로 평가될 경우(bool() 함수로 변환을 할 경우) False로 평가
- None인지 확인하는 방법: is 연산자 이용용하는 이유
>>> some = None
>>> bool(some)
False
>>> some is None
True
함수 - 인수
파이썬에서 매개변수로 인수를 전달하는 것은 다른 언어에 비해 유연한 방식을 가짐
- 위치 인수
- 함수 호출 시 매개변수의 순서대로 인자를 전달하는 방식
def divide(a, b):
return a / b
divide(10, 20) #매개변수 a에 10이, b에 2가 전달
- 키워드 인수
- 함수를 호출할 때 매개변수의 이름을 정하여 인자를 전달하는 방식
def average(a, b, c):
return (a + b + c) / 3
divide(15, c =10, b =11) # a에는 15, b는 11, c는 10이 전달
함수 - 매개변수
디폴트 매개 변수: 기본값을 갖는 매개 변수
def say_name(name, greeting = 'Hi~'): # <<<디폴트 매개변수
print(greeting + ' ' +name)
say_name('Hong')
say_name('Hong', 'Hello')
#결과
Hi~ Hong
Hello Hong
가변 매개변수: *
def print_args(*args): #인수 값들이 args매개변수에 튜플로 전달됨
print(args)
print(1,2,'Hi',True) # 인수
#결과
1 2 Hi True
키워드 매개변수: **
def print_kwargs(**kwargs): # 인수 값들이 kwargs 매개변수에 딕셔너리로 전달됨
print(kwargs)
print_kwargs(name = 'Hong', age =20, gender = 'M')
#결과
{'name': 'Hong', 'age': 20, 'gender': 'M'}
매개변수 주의사항
- 일반 매개변수 -> 디폴트 매개변수 -> 가변 매개변수 -> 키워드 매개변수 의 순서로 나열
- 가변 매개변수 및 키워드 매개변수는 한번에 하나씩만 사용 가능
잘못된 사용예
- 예 1) def f1(a=1, b): # 디폴트 매개 변수가 먼저 나옴
- 예 2) def f2(*a, *b): # 가변인자 매개 변수를 두 번 사용
- 예 3) def f3(**kw, *a): # 딕셔너리 매개 변수가 먼저 나옴
- 예 4) def f4(**a, **b): # 딕셔너리 매개 변수를 두 번 사용
가변 매개변수가 있는 함수 호출
def calc_total(*args):
t =0
for v in args:
t += v
return t
r1 = calc_total(1,2,3,4)
ls = [10, 20, 30, 40]
r2 = calc_total(*ls) #리스트 spread
print(r1, r2)
#결과
1 10
키워드 매개변수가 있는 함수 호출
def print_profile(**kwargs):
for k, v in kwargs.items():
print(str(k) + ': ' +str(v))
print_profile(name = '홍길동', age = 20, gender='남')
profile = {'name':'심청이', 'age':'18', 'gender':'여'}
print_profile(**profile) #딕셔너리 spread
#결과
name: 홍길동
age: 20
gender: 남
name: 심청이
age: 18
gender: 여
함수 - docstring
함수 몸체 시작 부분에 문자열을 포함시켜 함수에 대한 문서를 작성할 수 있는데 이를 docstring이라고 함.
docstring 사용 예
def print_profile(**profile):
'''
profile에 있는 키와 값을 출력하는 함수 입니다.
:param profile: 사용자의 프로필 정보가 있는 딕셔너리
:return: None
'''
print(print_profile.__doc__)
help(print_profile)
#결과
profile에 있는 키와 값을 출력하는 함수 입니다.
:param profile: 사용자의 프로필 정보가 있는 딕셔너리
:return: None
Help on function print_profile in module __main__:
print_profile(**profile)
profile에 있는 키와 값을 출력하는 함수 입니다.
:param profile: 사용자의 프로필 정보가 있는 딕셔너리
:return: None
함수 - 일등 시민
일등시민 (first-class citizen)
- 일등 시민이란 변수에 할당할 수 있거나, 인수를 통해 매개변수로 값을 전달할 수 있는 것을 말함
- 파이썬은 모든 것을 객체로 취급하며 함수도 일등 시민이기 땜누에 변수나 매개 변수로 전달이 가능
함수를 일등 시민으로 사용하는 예
def f1():
print('Hello world')
def f2(f):
f()
a = f1
a()
f2(f1)
#출력
Hello world
Hello world
f1의 함수는 a에 대입하고 a를 호출함
f2에다가 f1을 넘기면서 내부에서 인자를 받은 f를 실행하고 있음
즉, 내부에 f1을 받아서 실행중인것
함수 - 내부 함수
내부 함수(inner function)
- 함수 안에 정의된 함수
- 정의가 된 함수 내부에서만 사용 가능
def outer_fn(a, b):
def inner_fn(c):
return c ** 2
return inner_fn(a) + b
result = outer_fn(2, 3)
print(result)
#출력
7
함수 - 클로저
- 내부 함수를 사용하는 것은 클로저(Closure)를 활용하는 경우가 많음
- 클로저는 외부 함수의 매개변수 및 변수의 값을 내부함수에서 활용하도록 함 -> 외부함수가 종료되더라도 외부함수의 context를 유지
다음 예제의 코드가 어떻게 동작되는지 순서를 알아보자
def make_logger(tag):
def log(msg):
print(tag.upper() + ': ', msg)
return log #내부함수 자체를 리턴
error_logger = make_logger('error')
#return log 자체를 리턴함(내부함수)
info_logger = make_logger('info')
error_logger('hi~')
info_logger('Hello!!!')
#출력
ERROR: hi~
INFO: Hello!!!
함수 - 람다 함수
- 람다 함수(Lambda Function)는 단일문으로 표현되는 익명함수(Anonymous function)
- 함수 호출 시 인자로 함수를 전달할 수 있는데, 간단한 코드를 수행하는 함수를 매번 정의해서 호출하기는 불편 -> 이런경우 주로 사용되는 것이 람다 함수
- 람다 함수의 형식은 다음과 같다
lambda <parameter1>, ..., <parameterN> : <statement>
예제.. 예제를 보자!
def edit(word_list, editor):
return [editor(word) for word in word_list]
def to_upper(word):
return word.upper()
w1 = ['aaa', 'bbb', 'ccc']
result1 = edit(w1, to_upper)
result2 = edit(w1, lambda word: word.upper())
print(result1)
print(result2)
#출력
['AAA', 'BBB', 'CCC']
['AAA', 'BBB', 'CCC']
데코레이터
- 데코레이터(decirator)는 하나의 함수를 취해 또 다른 함수를 반환하는 함수
- 코드를 바꾸지 않으면서 함수 등에 기능을 추가
- 데코레이터를 위해 필요한 것들
- 가변 매개변수와 키워드 매개변수
- 내부 함수
- 함수 인수
예제1.
함수에 다음과 같은 기능을 추가해주는 데코레이터를 만들어 보자
- 함수 이름과 인자 값을 출력
- 인자로 함수를 실행
- 결과를 출력
def my_decorator(fn):
def func(*args, **kwargs):
print('Function Name: ', fn.__name__)
print('Positional Arguments: ', args)
print('Keyword Arguments: ', kwargs)
result = fn(*args, **kwargs)
print('Result: ', result)
return func
@my_decorator
def plus(a, b):
return a + b
print(plus(1,2))
#출력
Function Name: plus
Positional Arguments: (1, 2)
Keyword Arguments: {}
Result: 3
None
예제2.
함수에 다음과 같은 기능을 추가해주는 데코레이터를 만들어 보자
- 함수가 결과로 반환한 문자열을 <h1>.. </h1>태그로 감싸주는 데코레이터
def surround_h1(fn):
def func(*args, **kwargs):
return '<h1>' + fn(*args, **kwargs) + '</h1>'
return func
@surround_h1
def say_hello(name):
return 'Hello~, ' + name
print(say_hello('홍길동'))
#출력
<h1>Hello~, 홍길동</h1>
예제3.
앞의 예제를 데코레이터의 입력으로 들어간 태그로 감싸도록 업그레이드
def surround_tag(tag):
def decorator(fn):
def func(*args, **kwargs):
return '<' + tag + '>' + fn(*args, **kwargs) + '</' + tag + '>'
return func
return decorator
@surround_tag('p')
def say_hi(name):
return 'Hi~, ' + name;
print(say_hi('심청이'))
#결과
<p>Hi~, 심청이</p>
데코레이터 - 중복 사용
데코레이터는 중복해서 사용이 가능
- 여기서는 앞서 만든 데코레이터들을 중복해서 사용해 본다.
@surround_h1
@surround_tag('span')
def say_hi(name):
return 'Hi~,' + name
@my_decorator
@surround_tag('body')
@surround_h1
def say_hello(name):
return 'Hello~, ' + name
print(say_hi('심청이'))
say_hello('홍길동')
제너레이터
제너레이터(generator)는 파이썬의 시퀸스를 생성하는 객체
- 전체 시퀸스를 메모리에 한번에 생성하지 않고, 시퀸스를 순차적으로 생성하는 객체를 만들어 사용
- 만약 리스트에 엄청나게 많은 양의 데이터가 있다면?
- range() 함수도 제너레이터의 한 종류
제너레이터 함수
- 간단한 코드는 제너레이터 컴프리헨션을 사용
- 시퀸스를 생성하는 코드가 긴 경우는 제너레이터 함수를 사용
- 일반적인 함수처럼 작성을 하되 return 문 대신 yield 문으로 값을 반환
def my_range(first, last = None, step =1):
if last is None:
last = first
first = 0
while first < last:
yield first
first += step
ranger = my_range(1,10)
print('ranger', ranger)
for v in ranger:
print(v)
#결과
1
2
3
4
5
6
7
8
9
네임스페이스와 스코프
- 네임스페이스(namespace)는 이름이 사용되는 공간이며, 같은 네임스페이스 안에서는 특정 이름은 유일하게 사용
- 즉, 네임스페이스가 다르면 이름이 같다 하더라도 같은 서로 관계가 없음을 의미
- 같은 이름이라도 사용되는 위치에 따라 다른 것을 참조할 수 있음
- 함수는 하나의 네임스페이스
- 함수 내부의 변수와 함수 외부의 변수는 다른 네임스페이스에 존재
- 안 쪽의 네임스페이스에서 이름을 찾을 수 없는 경우 바깥 쪽으로 나가면서 이름을 찾음
- 스코프(scope)는 이름이 사용 될 수 있는 범위를 말한다.
네임 스페이스의 예: 아래와 같이 변수 name 및 함수들을 정의한 후 우측의 코드들을 실행
name = '홍길동'
def print_name1():
print(name)
def print_name2():
print(name)
name = '심청이'
print(name)
def print_name3():
name = '임꺽정'
print(name)
print_name1()
print(name)
#결과
홍길동
홍길동
print_name2()
print(name)
#결과
오류! >>> 전역 name이 쓰이기 전에 함수안의 name이 print로 호출되어 오류 발생
print_name3()
print(name)
#결과
임꺽정
홍길동
다음과 같이 전역 변수에 접근하여 사용할 수 있다.
(전역변수 name를 사용하고 싶으면 global선언을 한다)
def print_name4():
global name
name = '유관순'
print(name)
print_name4()
print(name)
#결과
유관순
유관순
네임스페이스의 내용 접근하기
- locals()함수: 로컬 네임스페이스의 내용이 담긴 딕셔너리 반환
- globals()함수: 전역 네임스페이스의 내용이 담긴 딕셔너리 반환
name = '홍길동'
def print_name5():
name = '심청이'
print('locals:',locals())
print(name)
print_name5()
print('globals:', globals())
#결과
홍길동
locals: {'name': '심청이'}
globals: {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000025924AD3760>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'C:\\Users\\dkxmp\\Documents\\GitHub_Store_main\\python practice\\ch5-pr6.py', '__cached__': None, 'name': '홍길동', 'print_name5': <function print_name5 at 0x0000025924FBCB80>}
이름에 "__"의 사용
- 두 개의 언더스코어(__)로 시작하고 끝나는 이름은 파이썬 내부에서 사용하기 위해 예약
- 따라서, 이름을 지을 때 __<이름>__과 같이 하면 안됨
- 사용이 불가능한 것은 아니지만, 권장하지 않음
- 예)
- 함수의 이름은 <함수이름).__name__에 저장
- 함수의 docstring은 <함수이름>.__doc__에 저장
예외처리
예외처리
- 프로그램에서 발생할 수 있는 오류를 처리하는 매커니즘
- 다음과 같이 생년을 입력하면 나이를 계산해 주는 함수의 경우
import detetime
def calc_age(birth_year):
return detetime.detetime.today().year #birth_year
생년을 입력할 때, 올해보다 더 큰 수를 입력한 경우, 나이를 그대로 음수로 리턴할 것인가?
잘못된 값을 전달하는 것을 막을 수 있는 방법은 --> 예외 이용
try/except 구문
- 예외가 발생하였을 때 프로그램이 비정상적으로 종료되지 않고, 계속 실행이 되게 하기 위해서는 try/except 구문을 사용
try:
#에외가 발생할 수 있는 코드
except:
#에외가 발생한 경우 처리할 코드
예)
student = {'심청이', '홍길동', '유관순'}
index = int(input('몇 번째 학생을 선택하나요?'))
try:
print('선택된 학생: ' + student[index])
except: #except Exception as e 와 같음
print('해당 학생이 존재하지 않습니다')
예외 타입에 따른 처리
- except만 사용할 경우 모든 종류의 예외를 처리
- 특정 종류의 예외를 따로 처리하고 싶다면 다음과 같은 형태로 사용
- except <예외 타입> as <이름>:
- # <예외 타입>에 해당하는 예외를 처리하는 코드
ls = [1,2,3]
while True:
value = input('Index [ q to quit]?')
if value == 'q':
break
try:
index = int(value)
print(ls[index])
except IndexError as err:
print('인덱스 범위 넒음:', index)
except Exception as other:
print('알 수 없는 에러 발생')
예외 만들기
- 예외를 만들기 위해서는 클래스를 알아야 하며, 여기서는 간단히 어떤 방식으로 예외를 생성하는지만 살펴봄
예외 클래스 작성
class BadBirthYear(Exception):
pass
예외 발생 시키기
import datetime
def calc_age(birth_year):
now = datetime.datetime.today().year
if birth_year > now:
raise BadBirthYear('The birth year cannot be greater than this year')
return now
예외를 처리하는 코드
try:
birth_year = int(input('Input your birth year: '))
age = calc_age(birth_year)
print('Your age is ', age)
except BadBirthYear as err:
print(err)
except Exception as e:
print('알 수 없는 에러')
#결과
Input your birth year: 2022
The birth year cannot be greater than this year
Summary
- 함수는 코드의 재사용, 모듈화, 가독성, 유지보수를 좋게 해준다
- 함수의 정의는 함수 머리와 몸체로 구성되어 있으며, 함수 몸체의 코드는 함수가 호출될 때 실행된다.
- 파이썬의 함수는 위치와 키워드 방식으로 인수 값을 매개변수로 전달 할 수 있다.
- 파이썬의 함수의 매개변수는 일반 매개변수, 디폴트 매개변수, 가변 매개변수, 키워드 매개변수가 있다.
- Docstring은 파이썬 함수에 대한 설명을 작성 할 수 있으며, 함수 사용자에게 함수에 대한 정보를 전달해 줄 수 있다.
- 파이썬의 함수는 일등 시민이다.
- 내부 함수는 함수의 내부에 정의된 함수이다.
- 클러저는 외부 함수의 context를 내부 함수가 유지하여 외부 함수의 매개변수나 변수에 저장된 값을 내부 함수에서 사용할 수 있도록 해준다.
- 람다 함수는 단일문으로 된 익명함수를 작성할 수 있도록 해주며, 일회성의 간단한 함수를 작성하기 용이한다.
- 데코레이터는 하나의 함수를 취해 다른 함수를 반환하는 함수로코드를 바꾸지 않으면서 함수에 기능을 추가할 수 있게 해준다.
- 제너레이터는 시퀸스를 생성하는 객체를 말하며, 전체 시퀸스를 메모리에 한번에 생성하지 않고 순차적으로 하나씩 생성한다.
- 제너레이터 함수는 제너레이터를 생성하는 함수로써 return 구문 대신 yield구문을 사용한다.
'Language > Python' 카테고리의 다른 글
[Python] Ch6 - part 2. 패키지 (0) | 2021.04.24 |
---|---|
[Python] Ch6 - part1. 모듈 (0) | 2021.04.24 |
[Python] Ch4 - part 3. 컴프리헨션 (0) | 2021.04.07 |
[Python] Ch4 - part 2. 반복문 (0) | 2021.03.29 |
[Python] Ch4 - part 1. 조건문 (0) | 2021.03.28 |