함수
- 어떠한 작업을 수행하기 위해 독립적으로 설계된 코드의 집합
- 재사용할 코드 묶음
함수 정의 방법
- 파라미터(parameter): 함수를 정의할 때 객체를 전달받는 변수(매개변수)
- 아규먼트(argument): 함수를 실행하는 쪽에서 함수에 넣어주는 값(인수)
- def <function_name>(parameter, ...):
code context
...
return <value> # 생략 가능 - 파라미터 정의와 `return`은 해도 되고 안해도 된다
>>> def get_std(num_list): # num_list 파라미터
>>> avg = sum(num_list) / len(num_list) # 평균 , avg는 지역변수
>>> deviation_list = []
>>> for num in num_list:
>>> deviation = avg - num
>>> deviation_list.append(deviation**2)
>>> var = sum(deviation_list) / len(deviation_list)
>>> return var ** 0.5
>>> num_list = [1,2,3,4,5,6]
>>> get_std(num_list)
1.707825127659933
>>> get_std([1,2,3,4,5,6])
1.707825127659933
#안되는 거
>>> get_std(num_list, num_list)
-> TypeError: get_std() takes 1 positional argument but 2 were given
>>> get_std()
-> TypeError: get_std() missing 1 required positional argument: 'num_list'
이름을 지을 때 관례 (Naming Rule)
- 함수이름은 동사로 시작하며, 사용자 입장에서 어떠한 기능을 추측할 수 있게 해야한다.
- 함수는 소문자로만 작성하며, snake case를 따른다. ( ex. `def add_number()` )
- 함수의 파라미터와 지역변수 둘중에 하나는 lower camel case를 따른다. (ex. userName , userAge )
변수의 사용 범위 (scope)
- local(지역변수): 함수 내부에 만들어진 지역 변수는 함수 내에서만 사용 가능 (파라미터도 동일!!)
- global(전역변수): 함수 밖에서 만들어진 변수는 어디에서든 사용 가능
- 함수내부에서 변수를 사용할 때는 지역변수로 선언하거나 파라미터로 받을것!!
안 좋은 예시
>>> gv = 10 # 전역변수
>>> def do_func():
>>> print(gv) # 절대 이렇게 하지 마세요!!
>>> do_func()
10
이렇게 하기
>>> def do_func():
>>> gv = 100 # 지역변수
>>> print(gv)
>>> do_func()
100
>>> print(gv)
10
>>> def do_func():
>>> loc = 10
>>> print(loc)
>>> do_func()
>>> print(loc)
-> NameError: name 'loc' is not defined
#함수 내에서 지역변수를 전역변수로 변경하는 방법
>>> def do_func():
>>> global gv_loc
>>> gv_loc = 100
>>> do_func()
>>> gv_loc
100
함수에 아규먼트를 넣는 방식
>>> def do_func(a,b,c):
>>> print(a,b,c)
#이렇게 전달 하는 방식을 positional argument 전달 방식
>>> do_func(1,2,3)
1 2 3
#이렇게 전달 하는 방식을 keyword argument 전달 방식
>>> do_func(a=1,b=2,c=3)
1 2 3
파라미터 정의하기
디폴트 파라미터
- 아규먼트를 넣어주지 않을 때 파라미터에 지정된 초기값을 사용
>>> def do_func(a,b,c=1004):
>>> print(a,b,c)
>>> do_func("hello",30)
hello 30 1004
다음의 리스트는 행과 열 형태로 구성되어 있다.
다음과 같은 행렬 형태의 리스트를 전달 받고,
추가로 열을 선택할 수 있는 정수를 전달 받아 열부분만 리스트에 담아 반환하는 함수를 만들어 주세요.
두번째 인수인 열부분을 선택할 수 있는 정수가 생략될 경우
0번째 열부분을 리스트에 담아 반환하면 됩니다.
lst = [[1,2,3], [4,5,6], [7,8,9]]
get_column(lst,1)
Output:
[2,5,8]
행열을 불러오는 함수
get_column | 열 |
get_index | 행 |
>>> lst = [
>>> [1,2,3],
>>> [4,5,6],
>>> [7,8,9]
>>> ]
#i=1이니까 두번째 열
>>> def get_column(lst,i=1):
>>> return [ n[i] for n in lst]
>>> get_column(lst)
[2, 5, 8]
가변 파라미터
- 함수를 정의하면서 아규먼트가 0개이상 들어갈 수 있게 해주는 파라미터
- *(asterisk)를 이용해서 정의하면 된다.
- 일반적으로 `*args` 로 표현
- 함수 내부에서는 튜플 형태로 묶임
>>> def do_func(*args):
>>> print(args)
>>> total = sum(args)
>>> return total
>>> do_func(1,2)
(1, 2)
>>> def do_func(a,*args,b=100):
>>> print(a)
>>> print(args)
>>> print(b)
>>> do_func(1,2,3,4,5,6,7,8,1000)
1
(2, 3, 4, 5, 6, 7, 8, 1000)
100
# keyword argument 를 활용해서 명시적을 전달해 주기
>>> do_func(1,2,3,4,5,6,7,8,b=1000)
1
(2, 3, 4, 5, 6, 7, 8)
1000
n개 이상(0개 포함)의 숫자를 아규먼트로 전달 받아 평균을 반환하는 함수를 만들어 주세요
단, 0개 아규먼트가 전달될 경우 None 반환!!!!
>>> def get_avg(*args):
>>> result = None
>>> if len(args) > 0:
>>> result = sum(args) / len(args)
>>> return result
# 파라미터 정의 * 의미는 패킹!!
>>> def get_avg(*args):
>>> return sum(args) / len(args) if len(args) > 0 else None
>>> print(get_avg(1,2,3,4,5,6))
3.5
# 언패킹 get_avg(*tup) == get_avg(1,2,3,4,5,6,67)
>>> tup = (1,2,3,4,5,6,67)
>>> get_avg(*tup)
>>> 12.571428571428571
>>> lst = [1,2,3,4,5,5]
>>> get_avg(*lst)
3.3333333333333335
>>> print(*lst)
1 2 3 4 5 5
packing , unpapacking
packing | 하나의 변수에 여러 객체를 묶어서 담는 방식 |
unpacking | 여러 객체를 묶고 있는 하나의 변수를 여러 변수에 풀어서 담는 방식 |
>>> lst1 = [1,2,3] # 패킹
>>> lst2 = [4,5,6] # 패킹
>>> for i1, i2 in zip(lst1,lst2): # 언패킹!!
>>> print(i1,i2)
1 4
2 5
3 6
>>> a,b = [1,2] # 언패킹
>>> print(a)
>>> print(b)
1
2
키워드 가변 파라미터
- 함수를 정의하면서 keyword argument가 n개가 들어갈 수 있다(0개 포함)
- 일반적으로 `**kwargs` 로 표현
- 함수 내부에서 딕셔너리 형태로 묶인다
- 변수명이 key, 값이 value가 된다
# ** 의미는 딕셔너리로 패킹한다는 의미
>>> def do_func(a,b,**kwargs):
>>> print(a)
>>> print(b)
>>> print(kwargs)
# c, karns, age는 파라미터로 정의 안되어 있으므로 kwargs 에 들어가게 됨
>>> do_func(10,b=20,c="hello",karns=100,age=43)
10
20
{'c': 'hello', 'karns': 100, 'age': 43}
>>> kwargs_dict = {
>>> "c" : "hello",
>>> "karns" : 100,
>>> "age" : 43
>>> }
#언패킹
>>> do_func(10,b=20,**kwargs_dict)
10
20
{'c': 'hello', 'karns': 100, 'age': 43}
>>> kwargs_dict = {
>>> "c" : "hello",
>>> "karns" : 100,
>>> "age" : 43,
>>> "a" : 10,
>>> "b" : 20
>>> }
# 언패킹, do_func(c="hello",karns=100,age=43,a=10,b=20)
>>> do_func(**kwargs_dict)
10
20
{'c': 'hello', 'karns': 100, 'age': 43}
람다 함수 (lambda)
- 한줄짜리 간단한 함수를 만들 때 사용
- 1회용 함수를 만들 때 많이 사용
>>> def add(num1,num2):
>>> return num1+num2
>>> add(1,2)
3
>>> add_lambda = lambda num1,num2: num1+num2
>>> add_lambda(1,2)
3
>>> my_max = max
>>> my_max([1,2,3])
3
>>>
# 하나의 인수를 받아 제곱해서 반환하는 람다 함수를 만들어 보자
>>> square = lambda x: x**2
>>> square(5)
25
클로져 (Closure)
- 함수 안에 함수를 정의 (내부함수)
- 함수를 둘러싼 상태(지역변수, 파라미터)를 기억했다 함수를 실행할 때 다시 꺼내서 사용하는 기법
>>> def do_outer(a,b):
>>> def do_inner(): # 내부함수
>>> return a+b
>>> return do_inner # 내부함수의 주소를 리턴!!
>>> inner_result1 = do_outer(10,20)
>>> inner_result2 = do_outer(30,50)
>>> print(inner_result1)
>>> print(inner_result2)
<function do_outer.<locals>.do_inner at 0x00000257070B5280>
<function do_outer.<locals>.do_inner at 0x00000257070B5430>
inner_result1()
30
inner_result2()
80
Callback Function
- 콜백 또는 콜백함수라고 한다
- 다른 코드의 인수로서 넘겨주는 실행코드
- 함수의 인수로 사용되는 함수
# 어떠한 집계를 할 수 있는 함수와 리스트를 입력받아 집계하는 함수
>>> def aggregating_data(func,lst):
>>> return func(lst)
>>> def avg(lst):
>>> result = sum(lst) / len(lst)
>>> return result
>>> aggregating_data(avg,[30,40,50])
40.0
>>> aggregating_data(lambda lst:sum(lst) / len(lst) , [30,40,50] )
40.0
>>> aggregating_data(sum,[30,40,50])
120
>>> map_obj = map(lambda x: x**0.5, [2,3,4,5,6,7,8])
>>> type(map_obj)
>>> map
>>> list(map_obj)
[1.4142135623730951,
1.7320508075688772,
2.0,
2.23606797749979,
2.449489742783178,
2.6457513110645907,
2.8284271247461903]
>>> map_obj = map(lambda x1,x2: x1+x2 , [1,2,3] , [4,5,6])
>>> list(map_obj)
[5, 7, 9]
Decorator
- 코드를 바꾸지 않고 공통기능을 추가하거나 수정하고 싶을 때 사용하는 기법
>>> def decorator_func(org_func): # 콜백함수를 받는다.
>>> def wrapper_func(): # 콜백함수를 실행하는 내부 함수를 구현
>>> print("오리지널 함수가 실행되기 전입니다.")
>>> org_func()
>>> return wrapper_func
>>> def do_func():
>>> print("오리지널 함수가 실행되었습니다.")
>>> result = decorator_func(do_func)
>>> result()
오리지널 함수가 실행되기 전입니다.
오리지널 함수가 실행되었습니다.
>>> def decorator_func(org_func): # 콜백함수를 받는다.
>>> def wrapper_func(): # 콜백함수를 실행하는 내부 함수를 구현
>>> print("오리지널 함수가 실행되기 전입니다.")
>>> org_func()
>>> return wrapper_func
>>> @decorator_func
>>> def do_func():
>>> print("오리지널 함수가 실행되었습니다.")
>>> do_func()
오리지널 함수가 실행되기 전입니다.
오리지널 함수가 실행되었습니다.