Sign In
Programming Language

[Python] 함수

Y
Yerim
Category
  1. Python

함수

입력값을 넣고 어떤 일을 수행한 후 그 결과물을 내어 놓는 것
똑같은 내용을 반복해서 작성하는 문제를 해결하기 위해 함수 사용
프로그램의 흐름을 잘 파악하고 오류를 찾기 쉬워진다

파이썬 함수 구조

def 함수_이름(매개변수): 수행할_문장1 수행할_문장2 ... return 리턴값
def 는 함수를 만들 때 사용하는 예약어
return은 함수의 결괏값을 리턴하는 명령어 (반환값이 없을 수 있다)
매개변수와 인수
def add(a, b): # a, b는 매개변수 return a+b c = add(3, 4) # 3, 4는 인수 print(c) # 7 d = add(b=7, a=5) # 매개변수를 지정하여 호출하는 경우 print(d) # 12
매개변수인수는 혼용해서 사용
매개변수: 함수에 입력으로 전달된 값을 받는 변수
인수: 함수를 호출할 떄 전달하는 입력값을 의미
입력값이 없는 함수가 있을 수 있다
매개변수를 지정하여 호출하는 경우 매개변수의 순서와 상관 없이 사용할 수 있다
def 함수_이름(*매개변수): 수행할_문장 ...
매개변수의 갯수를 모르는 경우 *매개변수로 설정
매개변수 앞에 *를 붙이면 입력값을 튜플로 만들어준다
def print_kwargs(**kwargs): print(kwargs) print_kwargs(a=1) # {'a': 1} print_kwargs(name='foo', age=3) # {'age': 3, 'name': 'foo'}
키워드 매개변수를 사용할 때는 매개변수 앞에 별 2개 **를 붙인다
a=1이 입력되면 kwargs는 {'a': 1}인 딕셔너리가 된다
# default1.py def say_myself(name, age, man=True): print("나의 이름은 %s 입니다." % name) print("나이는 %d살입니다." % age) if man: print("남자입니다.") else: print("여자입니다.")
매개변수에 초깃값 미리 설정
함수의 인자가 주어지지 않으면 초깃값이 사용된다
리턴값
def add_and_mul(a, b): return a+b, a*b
함수의 리턴값을 항상 1개 → 여러 개일 경우 튜플로 반환된다

함수 내부 변수

# vartest.py a = 1 def vartest(a): a = a + 1 vartest(a) print(a) # 1
# vartest.py def vartest(a): a = a + 1 vartest(a) print(a) # 오류
vartest 함수 내에서 매개변수 a는 함수 안에서만 사용하는 변수 → 외부 변수 영향 X
외부 변수를 변경하고 싶다면
# vartest_return.py a = 1 def vartest(a): a = a + 1 # return a a = vartest(a) print(a)
# vartest_global.py a = 1 def vartest(): global a # global a = a + 1 vartest() print(a)
return을 사용하여 계산한 값을 반환한다
global 명령어를 사용하면 외부 변수를 변경할 수 있다 (사용하지 않는 것이 좋음)

lambda

함수_이름 = lambda 매개변수1, 매개변수2, ... : 매개변수를_이용한_표현식
lambda는 함수를 생성하는 예약어로 def와 동일한 역할

내장 함수

함수명
기능
예시
abs
숫자의 절댓값 리턴
abs(3) # 3
abs(-3) # 3
abs(-1.2) # 1.2
all
반복 가능한 데이터 x를 입력값으로 받으며
x의 요소가 모두 참이면 True,
거짓이 하나라도 있으면 False를 리턴
all([1, 2, 3]) # True
all([1, 2, 3, 0]) # False
all([]) # True
any
반복 가능한 데이터 x를 입력으로 받아
x의 요소 중 하나라도 참이 있으면 True를 리턴,
x가 모두 거짓일 때만 False를 리턴
any([1, 2, 3, 0]) # True
any([0, ""]) # False
any([]) # False

일급 함수, 고차 함수, 람다 함수

일급 함수 First-Class Function

프로그래밍 언어가 함수를 ‘일급 시민 (값’으로 취급하는 것
함수를 다른 객체와 동일하게 취급
1.
함수를 변수에 할당 가능
2.
함수를 데이터 구조에 저장 가능
3.
함수를 인자로 다른 함수에 전달 가능
4.
함수를 결과로서 반환 가능
Python은 일급 함수를 지원 → 함수를 매우 유연하게 다룰 수 있다
# 함수를 변수에 할당 def say_hello(name): return f"Hello {name}" greet = say_hello print(greet("Alice")) # 출력: Hello Alice # 함수를 다른 함수의 인자로 전달 def greet_loudly(greeting_func): return greeting_func("Alice").upper() print(greet_loudly(greet)) # 출력: HELLO ALICE # 함수를 반환하는 함수 def get_greeting_func(): def greet(name): return f"Hello {name}" greet = get_greeting_func() print(greet("Alice")) # 출력: Hello Alice
함수를 변수에 할당, 다른 함수의 인자로 전달, 함수를 반환하는 함수를 만드는 등의 동작 가능
일급 함수를 사용하면 코드가 더울 유연
고차 함수와 같은 패턴 구현 가능

고차 함수 Higher-Order Function

다른 함수를 인자로 받거나, 결과로서 함수를 반환하는 함수를 의미
‘일급 함수’의 특성을 활용하는 프로그래밍 패턴 → 함수형 프로그래밍에서 많이 사용
Python에서는 일급 함수를 지원 → 고차 함수를 쉽게 사용할 수 있다
map(), filter() 등의 내장 함수는 모두 고차 함수
# 함수를 인자로 받는 함수 def apply_to_three(func): return func(3) def square(n): return n ** 2 print(apply_to_three(square)) # 출력: 9 # 함수를 반환하는 함수 def apply_addr(n): def add(x): return x + n return add add_five = make_addr(5) print(Add_five(3)) # 출력: 8 # map 함수는 함수와 반복 가능한 객체를 인자로 받아, # 해당 함수를 각 요소에 적용한 결과를 반환하는 고차 함수 numbers = [1, 2, 3, 4] squares = map(square, numbers) print(list(squares)) # 출력: [1, 4, 9, 16]
코드의 재 사용성을 높이고 추상화 수준을 높여 코드의 가독성을 향상
명령형 스타일로 작성된 코드를 선언적인 스타일로 리팩토링 가능

람다 함수

이름이 없는 일회용 함수를 생성하는 데 사용
lambda 키워드를 사용하여 작성, 작고 간단한 함수를 필요로 하는 곳에 사용
lambda vs def
1.
def 키워드를 사용하여 함수를 정의하는 대신 lambda 키워드를 사용
2.
람다 함수는 이름이 없습니다 (익명 함수)
3.
람다 함수는 주로 한 줄로 표현, 복잡한 로직을 포함하지 안흔ㄴ다
람다 함수 기본 구조
lambda arguments: expression
람다 함수 예시
# 숫자를 받아서 그 제곱을 반환하는 람다 함수 square = lambda x: x ** 2 print(square(5)) # 출력: 25 # 두 숫자를 더하는 람다 함수 add = lambda x, y: x * y print(add(3, 4)) # 출력: 7
numbers = [1, 2, 3, 4] squares = map(lambda x: x ** 2, numbers) print(list(squares)) # 출력: [1, 4, 9, 16]
map(), filter(), reduce() 등의 고차원 람다 함수를 인자로 전달하는 경우가 많다
간단한 로직을 빠르고 쉽게 작성 가능
복잡한 로직을 처리해야하는 경우 일반 함수를 사용하는 것이 가독성이 더 좋다

클로저

특정 함수와 그 함수가 생성된 환경을 결합한 것
Python에서만 factory function이라고 불린다
클러저는 함수가 생성된 시점의 범위에 있는 모든 변수를 기억하고 이에 접근
Python에서 클로저
1.
함수 내부에 함수가 정의, 이러한 내부 함수를 중첩 함수라고 부른다
2.
내부 함수는 외부 함수의 변수를 참조
3.
외부 함수는 내부 함수를 반환
클로저 예시
# 클로저가 아닌 경우 def outer_functions(): def inner_function(): return 100+100 return inner_function # 클로저의 경우 def outer_function(x): def inner_function(y): return x + y return inner_function inner = outer_function(100) innter(200) # inner 입장에서 100을 변경할 수 있는 방법은 없다
outer_functioninner_function을 반환
innter_function은 외부 함수와 변수 x를 참조, outer_function을 호출,
결과를
closure에 저장 →inner_function의 코드와 x=10이라는 상태 기억
클로저 활용
1.
데이터 은닉: 외부에서 직접 접근할 수 없는 변수를 ‘감추는’ 방법을 제공 (데이터 은닉, 캡슐화 구현 가능)
2.
지연 바인딩: 함수가 실행될 때 그 함수의 환경을 기억 → 함수의 행동을 호출 시점의 상황에 따라 동적으로 변경하는 것이 가능
3.
함수형 프로그래밍과 데코레이터: 함수를 반환하는 능력 덕분에 고차 함수와 데코레이터의 구현에 필수적 (데코레이터는 원래 함수의 행동을 변경하지 않고 기능을 추가하거나 수정하는 데 사용)

데코레이터

Python 2.4 버전에서 도입된 오래된 기능
함수나 메소드에 추가 기능을 동적으로 부여할 때 사용 → @ 기호와 함께 사용
데코레이터로 지정된 함수가 바로 아래에 정의된 함수에 적용
데코레이터 적용 원리
데코레이터는 고차 함수 higher-order function
하나 이상의 함수를 인자로 받고 함수를 결과로 반환하는 함수
데코레이터는 하나의 함수를 인자로 받아 기능이 추가 또는 변경된 새로운 함수를 반환
데코레이터 예시
def simple_decorator(function): def wrapper(): print("Before the functions call") function() print("After the function call") @simple_decorator def hello(): print("Hello, world!") hello() # 데코레이터가 없는 상태에서는 simple_decorator(hello)()와 같다 simple_decorator(hello)() # 이전 wrapper()와 동일
Before the function call Hello, world! After the function call
다른 예시
def simple_decorator(function): def wrapper(1): print("Before the functions call") function() print("After the function call") return wrapper @simple_decorator def 합(1): return sum(1) 합([1, 2, 3, '4'])
매개변수가 있는 함수의 데코레이터
def debug(function): def new_function(): print(f'{function.__name__} 함수 시작') function() print(f'{function.__name__} 함수 끝') return new_function @debug def sum_1_to_n(n): return n * (n + 1) / 2 result = sum_1_to_n(30) print(result)
debug 데코레이터를 붙여서 실행하면 에러 발생
def debug(function): def new_function(n): print(f'{function.__name__} 함수 시작') print(n) print(f'{function.__name__} 함수 끝') return new_function @debug def sum_1_to_n(n): return n * (n + 1) / 2 result = sum_1_to_n(30) print(result)
sum_1_to_n 함수 시작 30 sum_1_to_n 함수 끝 None
def debug(function): def new_function(*args, **kwargs): print(f'{function.__name__} 함수 시작') function(*args, **kwargs) print(f'{function.__name__} 함수 끝') return new_function
어떤 형태의 인자를 사용하든 상관 없이 사용할 수 있는 데코레이터를 붙이려면 new_function을 수정해야한다
리턴 값이 있는 함수의 데코레이터
def debug(function): def new_function(*args, **kwargs): print(f'{function.__name__} 함수 시작') function(*args, **kwargs) print(f'{function.__name__} 함수 끝') return new_function @debug def sum_1_to_n(n): return n * (n + 1) / 2 result = sum_1_to_n(30) print(result)
sum_1_to_n 함수 시작 sum_1_to_n 함수 끝 None
중첩 데코레이터
@decorator1 @decorator2 ... @decoratorN def function(*args, **kwargs): pass
여러 개의 데코레이터를 붙일 수 있다
def decorator1(function): def new_function(*args, **kwargs): print('첫 번째 데코레이터 시작') result = function(*args, **kwargs) print('첫 번째 데코레이터 끝') return result return new_function def decorator2(function): def new_function(*args, **kwargs): print('두 번째 데코레이터 시작') result = function(*args, **kwargs) print('두 번째 데코레이터 끝') return result return new_function def decorator3(function): def new_function(*args, **kwargs): print('세 번째 데코레이터 시작') result = function(*args, **kwargs) print('세 번째 데코레이터 끝') return result return new_function @decorator1 @decorator2 @decorator3 def sum_1_to_n(n): return n * (n + 1) / 2 result = sum_1_to_n(30) print(f'result: {result}')
첫 번째 데코레이터 시작 두 번째 데코레이터 시작 세 번째 데코레이터 시작 세 번째 데코레이터 끝 두 번째 데코레이터 끝 첫 번째 데코레이터 끝 result: 465.0
다른 예시
def decorator1(function): def new_function(): print('첫 번째 데코레이터 시작') function() print('첫 번째 데코레이터 끝') return result return new_function def decorator2(function): def new_function(): print('두 번째 데코레이터 시작') function() print('두 번째 데코레이터 끝') return result return new_function def decorator3(function): def new_function(): print('세 번째 데코레이터 시작') function() print('세 번째 데코레이터 끝') return result return new_function @decorator1 @decorator2 @decorator3 def print_hello(): print('hello world!') print_hello()
첫 번째 데코레이터 시작 두 번째 데코레이터 시작 세 번째 데코레이터 시작 hello world! 세 번째 데코레이터 끝 두 번째 데코레이터 끝 첫 번째 데코레이터 끝 465.0
동적 데코레이터
def add(function): def new_function(*args, **kwargs): result = function(*args, **kwargs) return result + 100 return new_function @add def plus(a, b): return a + b result = plus(10, 20) print(f'result: {result}') # result: 130
add를 데코레이터로 사용하여 원래 함수의 결과에 100을 더하도록 변한다
함수의 결과에 100이 아닌 다른 숫자를 더하는 데코레이터가 필요한 경우 비슷한 로직의 데코레이터를 동적으로 생성하는 것이 가능하다
def add(n): def decorator(function): def new_function(*args, **kwargs): result = function(*args, **kwargs) return result + n return new_function return decorator
add 함수는 decorator함수를 리턴하는 고위 함수
decorator함수는 function 함수를 인자로 받아 new_function 함수를 리턴하는 고위 함수
add 함수는 decorator라는 고위함수를 리턴하는 고위함수
→ 인자 n을 받아 고위 함수를 동적으로 생성
@add(10) def plus(a, b): return a + b result = plus(10, 20) print(f'result: {result}') # result: 40
add() 호 안의 수를 바꾸어 넣으면 plust 함수가 변경될 수 있다
클래스형 데코레이터
class Debug: def __init__(self, function): self.function = function def __call__(self, *args, **kwargs): print(f'{self.function.__name__} 함수 시작') self.function() print(f'{self.function.__name__} 함수 끝')
debug 데코레이터를 클래스 형식으로 작성한 경우
@Debug def f1(): print('안녕하세요') @Debug def f2(): print('hello') f1() f2()
f1 함수 시작 안녕하세요 f1 함수 끝 f2 함수 시작 hello f2 함수 끝
데코레이터의 활용
다양한 상황에서 유용하게 사용 가능
함수의 실행 시간 측정 → 로깅을 수행하는 경우 사용자의 접근 권한 확인
Subscribe to 'Yerim-DevNote'
Subscribe to my site to be the first to receive notifications and emails about the latest updates, including new posts.
Join Slashpage and subscribe to 'Yerim-DevNote'!
Subscribe
👍