파이썬 프로그래밍를 읽고, 정리한 글입니다.
목차
· 함수 중심 프로그래밍
· 내장 함수
· 사용자 정의 함수
· 변수의 활동 범위
· 가변 인수 처리
· 클로저
· 람다 함수
· 일급 함수
· 함수 장식자
· 재귀 함수
함수 중심 프로그램
· 함수 중심(지향) 프로그래밍은 대부분의 소스 코드를 함수로 작성하여 문제를 해결하는 프로그래밍 기법으로, 소스의 내용을 성격 따라 정리해서 볼 수 있도록 한다.
· 함수를 사용하면 한 번의 코드 작성으로 다양한 상황에서 호출할 수 있어 코드 재사용성이 높아지고, 코드의 수정 및 유지 보수도 쉬워진다.
· 함수의 이름은 객체변수로 기억 장소에 대한 장소를 기억한다.
내장 함수
· 내장 함수(built-in function)는 프로그램을 실행할 때 기본적으로 주기억장치에 로딩되는 함수로, 언제든지 사용할 수 있다.
- 즉, 인터프리터에 포함되어 있어 별도의 로딩 과정 없이 바로 사용할 수 있는 기본 함수를 말한다.
· 자주 사용되는 내장함수:
함수명 | 기능 |
sum(iterable) | 숫자들의 합을 반환 |
input(prompt) | 사용자 입력을 문자열로 반환 |
globals() | 전역변수의 리스트를 반환 |
del(x) 혹은 del x | 객체변수를 삭제 |
eval(expr) | 수식 모양의 문자열을 파이썬 수식으로 변환 |
hex(x) | 정수 x의 16진수 값을 구해서 '문자열'로 반환 |
oct(x) | 정수 x의 8진수 값을 구해서 '문자열'로 반환 |
bin(x) | 정수 x의 2진수 값을 구해서 '문자열'로 반환 |
int(x) | 정수로 형 변환하여 결과를 반환 |
float(x) | 실수로 형 변환하여 결과를 반환 |
all(iterable) | iterable의 모든 요소가 참일 경우 True를 반환 |
any(iterable) | iterable의 하나 이상의 요소가 참일 경우 True를 반환 |
max(iterable) | 최댓값을 반환 |
min(iterable) | 최솟값을 반환 |
round(x) | 반올림을 반환 |
zip(*iterable) | 동일한 개수로 이루어진 iterable 객체를 묶어 준다. |
· 예시1 - 내장 함수 사용
# 리스트 요소의 합을 반환
print(sum([3, 5, 7]))
# 실수를 정수화
print(int(1.7))
# 정수를 실수화
print(float(3))
# 숫자를 문자화
print(str(5) + ': 숫자 오')
a = 10
# 숫자 모양의 문자열을 수식화
print('eval 결과:', eval('a + 5'))
# 반올림한 결과를 반환
print(round(1.2), round(1.6))
# iterable 요소 참, 거짓 확인
b_list2 = [1, 3, 2, 5, 7, 6]
print('모든 숫자가 10 미만인가? ', all(a < 10 for a in b_list2))
print('숫자 중 3 미만이 있는가? ', any(a < 3 for a in b_list2))
# x와 y 두 개의 리스트에 zip()을 사용하여 튜플 작성
x = [10, 20, 30]
y = ['a', 'b']
for i in zip(x, y): # 30은 대응되는 짝이 없어 제외된다.
print(i)
· 예시2 - globals 함수 사용하기
- 현재 파일에서 사용하고 있는 객체의 목록들을 확인하려면 내장 함수인 globals를 사용한다.
print('현재 파일의 객체 목록:', globals())
- 개발자가 직접 만든 함수와 파이썬이 내부적으로 만든 객체들을 볼 수 있다.
· 예시3 - 외장 함수 사용
- 파이썬을 설치하면 사용할 수 있으나 내장 함수만큼 사용 빈도가 높지 않은 함수나 별도의 모듈로 지원하는 함수는 import 구문을 이용해 특정 모듈로 로딩하면 사용할 수 있다. 이를 외장 함수라고 부른다.
# 수학 관련 함수를 담고 있는 모듈 로딩
import math
# 근사치 중 큰 장수 반환
print(math.ceil(1.2), math.ceil(1.6))
# 근사치 중 작은 정수 반환
print(math.floor(1.2), math.floor(1.6))
사용자 정의 함수
· 내장 함수만으로 프로그램을 작성하기 어려울 때, 직접 함수를 만들어 사용할 수 있다. 이를 사용자 정의 함수라고 부른다.
· 사용법:
- 사용자 정의 함수의 선언은 키워드 def로 시작하고, 그 뒤에 함수명, 소괄호가 차례로 나온다. 소괄호 안에는 매개변수(parameter)를 적어줄 수 있고, 매개변수가 여러 개면 쉼표를 사용해 구분한다.
- 매개변수는 해당 함수 영역 안에서만 사용할 수 있는 지역변수가 된다.
- 함수 명명 규칙은 변수명 규칙과 같다. https://scshim.tistory.com/555
- def 함수명(): 이라는 함수 정의의 첫 줄을 헤더라고 부르고, 나머지 함수 부분은 바디라고 부른다.
- 헤더는 콜론으로 끝나야 하고, 바디 영역에 적는 수행문은 들여쓰기(Tab or Space Bar)해야 한다. 수행문의 개수는 제한이 없다.
- 함수 내에 'return 반환값' 형태의 수행문이 사용되었다면 해당하는 값을 반환하고, return 문이 없다면 None 값을 반환한다.
사용자 정의 함수를 작성하고 호출하기
· 예시1 - 하나의 매개변수를 갖고, return문이 있는 함수
def func1(arg):
return arg
print(func1(1))
· 예시2 - 두 개의 매개변수를 갖고, return문이 없는 함수
# 반환할 return 문이 없는 함수
def func2(arg1, arg2):
arg1 + arg2
# 함수가 실행은 되나 아무런 결과가 없다.
print(func2(10, 20))
- 함수에서 reutrn 문을 사용하지 않으면, return None이 생략한 형태라고 보면 된다.
· 예시3 - 함수 블록 탈출과 return 문
def func3(arg):
if arg % 2 == 0:
return
print(arg)
func3(4)
func3(5)
- 함수 수행 중 return 문을 만나면 무조건 함수 진행이 종료되고 함수를 호출한 지점으로 수행 순서가 이동한다.
· 예시4 - 함수명과 주소
def func1():
print('func1 호출')
func1()
# 함수명은 객체 주소를 기억한다.
print(func1)
# 함수 주소를 치환
other = func1
# func1과 동일한 결과를 얻는다.
print(other)
- 파이썬은 모든 값이 객체로 취급되고, 함수도 마찬가지로 객체로 취급된다. 사용자 정의 함수명은 함수로 만들어진 객체의 주소를 갖고 있다.
- '변수 = 함수명'을 한면 변수는 함수명이 가진 객체의 주소를 치환받는다. 함수 객체는 별명을 하나 더 갖는다고 보면 된다.
· 예시5 - 인자가 여러 개인 return문
def swapfunc(a, b):
return b, a
a = 1
b = 2
print(swapfunc(a, b))
- 함수의 반환 값은 언제나 한 개다. 'return 값1, 값2' 형식으로 작성한 것은 'return (값1, 값2)'와 같다. 반환값은 튜플로 만들어져 한 개의 묶음으로 반환된다.
· 매개변수의 유형
- 함수를 호출할 때 입력값을 받아 해당 함수의 수행 작업을 하는 매개변수의 유형은 네 가지가 있다.
1. 위치 매개변수(positional arguments): 인수와 순서대로 대응
2. 가본값 매개변수(default arguments): 매개변수와 입력이 없으면 기본값을 사용
3. 키워드 매개변수(keyword arguments): 인수와 파라미터를 동일 이름으로 대응
4. 가변 매개변수(arbitrary arguments): 인수의 개수가 동적인 경우
함수 호출 방법의 다른 형태
※ if 조건문으로 함수 호출
· 함수를 if 조건문에서 사용하면 편리할 때가 있다.
def isodd(arg):
return arg % 2 == 1
result = {x: x * 2 for x in range(1, 11) if isodd(x)}
print(result)
- for 반복 처리로 1~10 사이의 숫자가 차례대로 if 조건에 적어 둔 isodd 함수의 인자 x에 대응된 후 홀수면 True, 짝수면 False를 반환한다. 함수 값이 False이면 아무런 작업을 하지 않고, True이면 홀수만 제곱하여 출력한다.
※ 내부 함수 호출
· 함수 안에 함수를 만들 수 있다. 이를 내부 함수라고 부른다.
· 내부 함수는 외부 함수의 멤버이므로 외부 함수에서 내부 함수를 실행하도록 해야 한다.
def outerfunc():
def innerfunc():
print('내부 함수')
innerfunc() # 내부 함수를 실행
outerfunc() # 외부 함수 실행
변수의 활동 범위
· 변수가 어느 영역(모듈, 함수, 클래스)에서 선언되었는지 또는 치환되었는지에 따라 변수의 활동 범위가 달라진다.
- 함수 안에 변수가 선언되면 해당 함수 안에서만 유효한 지역변수가 된다.
- 변수를 모듈(보통은 파일로 저장) 수준으로 선언하면 해당 파일의 어디에서든 호출이 가능한 전역변수가 된다.
job = '기획자' # 전역변수
def funJob():
name = 'Ted' # 함수 내에서만 유효한 지역변수
job = '개발자' # 지역변수
print(name, job)
funJob()
명령어 nonlocal로 변수의 참조 수준 변경
· nonlocal 명령어는 지역 변수가 아님을 선언한다.
· nonlocal 명령어를 사용하면, nonlocal 이 사용된 함수 바로 한단계 바깥쪽에 위치한 변수와 바인딩을 할 수 있다.
def kbs():
a = 1 # kbs 함수에서 유효한 지역변수
def mbc():
nonlocal a # a는 mbc 함수의 외부 함수인 kbs 함수의 a 변수가 된다.
print(a)
a = 2
mbc()
kbs()
명령어 global로 변수의 참조 수준 변경
· global 명령어는 적용된 변수를 전역변수로 만든다.
a = 1 # 전역변수 a
def mbc():
global a # a는 전역변수가 된다.
print(a) # global이 없으면 UnboundLocalError 에러
a = 2
mbc()
· 예시 - global과 nonlocal 명령어 사용하기
a = 10;b = 20;c = 30 # 전역변수
print('함수 수행 전 a:{}, b:{}, c:{}'.format(a, b, c))
def foo():
# foo 함수 영역에서만 유효한 지역변수
a = 40
b = 50
def bar():
# b = 60 -> bar 함수 영역에서만 유효한 지역변수
# c = 70
nonlocal b # b는 bar의 외부 함수인 foo의 b가 된다.
global c # c는 전역변수를 의미
print('bar에서 출력1) a:{}, b:{}, c:{}'.format(a, b, c))
b = 80
print('bar에서 출력2) a:{}, b:{}, c:{}'.format(a, b, c))
c = 90
print('bar에서 출력3) a:{}, b:{}, c:{}'.format(a, b, c))
bar()
print('foo에서 출력 a:{}, b:{}, c:{}'.format(a, b, c))
foo()
print('함수 수행 후 a:{}, b:{}, c:{}'.format(a, b, c))
- 전역변수를 함수 안에서 사용하려면 함수 안에서 global 변수명이라고 적어 주면 함수 안에 선언된 변수는 지역변수가 아니라 전역변수로 인정된다.
- 내부 함수의 경우 nonlocal 변수명으로 해당 변수의 수준을 외부 함수로 설정할 수 있다.
함수를 호출할 때 named argument로 매핑
· 함수를 선언하면서 매개변수에 디폴트 값을 적어줄 수 있다. 만약 전달되는 인수 없이 함수를 호출하면 지정된 디폴트 매개변수로 대체되어 처리된다.
· 함수를 호출하면서 함수명처럼 인수에 초깃값을 주고 매개변수의 이름과 매핑시켜 전달할 수 있다. 이 방법을 사용하면 함수의 매개변수에 인수를 전달할 때 매핑 순서를 지키지 않아도 된다.
· 예시
def showplus(start, end=5):
print(start + end)
showplus(2, 3)
showplus(3)
showplus(start=2, end=3) # 동일한 이름의 인수와 매개변수가 매핑
showplus(end=4, start=3) # 동일한 이름의 인수와 매개변수가 매핑
showplus(2, end=3)
# 두 번째 인자가 상수면 에러 발생
# showplus(start=2, 3)
# showplus(end=4, 3)
가변 인수 처리
· 매개변수 이름에 *를 붙여 주면 입력값을 전부 묶어 튜플로 처리하게 된다. *를 사용한 packing은 하나의 매개변수에 여러 개의 값을 넣는 것이 가능하다.
· 딕셔너리 자료형을 처리하려면, 매개변수명 앞에 **를 주면 된다.
· 예시1 - 입력값을 튜플로 처리하기
def fun1(*ar): # 매개변수에 *을 사용
print(ar) # 튜플로 처리
for i in ar: #튜플 자료를 반복문으로 하나씩 출력
print('음식:' +i)
# 가변적인 인수 개수
fun1('공기밥')
fun1('비빔밥', '김밥')
print()
def fun2(a, *ar):
print(ar)
for i in ar:
print(a + ':'+ i)
fun2('과일','사과', '배')
fun2('과일','사과', '배', '바나나')
# 다음은 오류 발생
# def fun2(*ar, a)
· 예시2 - 딕셔너리 자료형 매핑
def func(w, h, **other): # other은 딕셔너리 형 자료와 매핑
print('몸무게 {}. 키 {}'.format(w,h))
print(other)
func(64, 175, name='Ted', age=23)
func(50, 175, name='Robin')
print()
# 함수의 매개변수에 인수를 전달할 때 *, ** 혼합도 가능
def functotal(a, b, *v1, **v2):
print(a, b)
print(v1)
print(v2)
functotal(1, 2)
functotal(1, 2, 3, 4, 5)
functotal(1, 2, 3, 4, 5, m=6, n=7)
클로저
· 클로저(closure)는 함수 밖에서 함수 안의 지역변수에 접근할 수 있도록 하는 구조를 의미하며, 스코프(scope)에 제약을 받지 않는 변수들을 포함하고 있는 코드 블록이다.
· 사용 목적: 함수 수행 후에도 지역 변수를 참조하여 사용할 수 있도록 만든다. 기본적으로 함수 안에 선언된 지역변수를 함수 수행이 끝나면 소멸한다.
· 클로저 사용법: 함수 안에 함수를 선언하고 내부 함수의 주소를 반환하도록 구현한다.
def 외부 함수
지역변수
...
def 내부 함수
지역변수
...
return 내부 함수명 # 클로저
- 내부 함수의 주소를 반환함으로써 함수 내의 변수를 함수 밖에서 계속 유지할 수 있는 구조가 클로저다.
- 전역변수를 사용하지 않도고 함수 내의 지역변숫값을 지속해서 기억하고 변경된 최신 상태를 유지할 수 있기 때문에 내부 데이터를 은닉하기 위해 사용할 수 있다.
· 예시1
def outer():
count = 0
def inner(): # 내부 함수
nonlocal count
count += 1
return count
return inner # 클로저를 통해 내부 함수의 주소를 반환한다.
# 함수 내의 count 값 확인하기
var1 = outer() # 객체 생성 후 내부 함수의 객체 주소를 var1에게 치환
print(var1()) # 변숫값이 계속 유지
print(var1()) # 변수 count의 값이 계속 유지
var2 = outer() # 새로운 객체 생성
print(var2())
- var1 = outer()하면 내부 함수객체가 만들어지고, 변수 var1이 내부 함수 객체의 주소를 갖게 된다. 즉, 함수가 호출될 때마다 함수 내에서 선언된 변수가 가진 값이 초기화되는 것이 아니라 값이 계속 유지된다.
· 예시2 - 분기별로 상품 총액에 대한 세금을 다르게 적용하기
def outer2(tax): # 매개변수 tax는 outer2 함수의 지역변수
def inner2(amount, unitPrice):
amount = amount * unitPrice * tax # 수량 * 단가 * 세금
return amount # inner2 함수는 amount를 반환
return inner2 # outer2는 inner2 함수의 주소를 반환 (클로저)
# 1분기에는 수량 * 단가에 tax를 0.1 부과
q1 = outer2(0.1) # inner2 함수의 객체 주소 기억
print('result1: ', q1(5, 10000))
print('result2: ', q1(10, 20000))
# 2분기에는 수량 * 단가에 tax를 0.05 부과
q2 = outer2(0.05) # inner2 함수의 객체 주소 기억
print('result3: ', q2(5, 10000))
print('result4: ', q2(10, 20000))
람다 함수
· 익명함수 또는 축약함수라고 부르며 이름이 없고, return 문 없이 결과를 반환하는 단순한 구조의 함수다.
· 사용 목적: 함수를 쓰고 싶은데 이름을 정하지 않고, 일회용으로 사용하고 싶을 때가 있다. 이렇게 하면 소스의 길이를 줄이는 효과가 있고, 개발자의 의도가 명확히 드러나므로 가독성이 향상된다.
· 사용법:
lambda 인자... :표현식 # return 없이 결과를 반환
· 예시 1
· 예시 2
def hap(x, y):
return x + y
print(hap(1, 2))
# 위 코드를 람다로 표현
print((lambda x, y: x + y)(1, 2))
lambdaFun1 = lambda x, y: x + y # 람다 함수를 변수에 치환
print(lambdaFun1(3, 4))
# 람다도 인수에 초깃값 지정 가능
lambdaFun2 = lambda x, y=10: x + y
print(lambdaFun2(5))
print(lambdaFun2(5, 6))
# 람다도 가변인수 지정 가능
lambdaFun3 = lambda x, *y, **z : print(x, y, z)
lambdaFun3(1, 2, 3, m=4, n=5)
# 다른 함수(filter)에서 람다 함수 사용하기
result = list(filter(lambda a: a < 5, range(10)))
print(result)
print(list(filter(lambda a:a%2, range(10)))) # 홀수 출력
- 람다 함수는 자체적으로 처리 결과를 출력할 수 있고, 수식을 변수에 치환해 변수명()으로 처리해도 된다. 치환한 것은 함수객체의 주소이므로 제대로 처리된다.
일급 합수
· 어떤 객체가 있을 때 다음 조건을 만족하면, 일급 함수를 지원하는 프로그래밍 언어로 취급한다.
1. 함수를 변수나 데이터 구조 안에 담을 수 있다.
2. 함수 안에 함수를 선언할 수 있다.
3. 함수의 매개변수로 함수를 전달할 수 있다.
4. 함수를 반환값으로 사용할 수 있다.
· 파이썬은 함수 중심(지향) 언어이므로 일급 함수를 지원한다.
· 예시
def func1(a, b):
return a + b
func2 = func1 # 함수의 수행 결과가 아닌 주소를 치환
print(func1(3, 4)) # 함수를 호출한 후 반환값 출력
print(func2(3, 4))
def func3(func): # 함수의 매개변수로 함수를 받는다.
def func4():
print('내부 함수 실행')
func4() # 내부 함수 실행
return func # 함수의 반환값이 함수
savedFun = func3(func1)
print(savedFun(3,4))
함수 장식자
· 함수 감싸기(wrapping)을 해 주는 패턴으로, 함수 장식자(decorator)는 다른 함수를 감싼 함수다.
- 첫 번째 함수가 호출되면 반홥값이 장식자에게 건네진다. 그러면 장식자는 포장된 함수로 교체하여 함수를 돌려 준다.
· 장식자는 함수를 변형시키는 방법을 기술하는 데 편리하다. 장식자는 본질적으로 장식하는 함수의 기능을 강화하는 메타(어떤 정보를 내포) 프로그래밍 기법이다.
- 즉, 함수 장식자란 이미 정의된 함수로 다른 함수를 장식하는 데 사용하는 것으로, 장식자를 프로그램하는 것은 기본적으로 장식된 함수가 특정 함수로 전달될 수 있도록 해 준다.
· 사용 목적:
1. 기존 함수에 기능을 추가하고, 새로운 함수를 만들 수 있으므로 함수의 내부를 수정하지 않고 기능에 변화를 주고 싶을 때 사용한다.
2. 어떤 동작을 함수 앞뒤에 수행하거나, 공통으로 사용하는 코드를 쉽게 관리하는 목적으로 적용하면 효과적이다.
· 예시1
def make2(make1fn):
# make1이 실행된 "안녕 반가워 Ted"를 반환하는 람다 함수의 주소를 반환
return lambda: "안녕 " + make1fn()
def make1(hellofn):
# hello가 실행된 "반가워 Robin"을 반환하는 람다 함수의 주소를 반환
return lambda: "반가워 " + hellofn()
def hello():
return "Ted"
hi = make2(make1(hello)) # 일반적인 함수 처리 방법
print(hi())
# 함수 장식자 처리 방법
@make2
@make1
def hello2():
return "Robin"
hi2 = hello2() # 함수 실행 결과를 치환
print(hi2)
- 일급 함수는 함수를 호출하면서 인수를 이용하여 매개변수에 함수를 전달할 수 있다. 위 예시의 make2(make1(hello))의 수행 순서를 보면 make1의 매개변수로 hello 함수의 주소가, make2의 매개변수로 make1 함수의 주소가 전달된후 최종적으로 make2 함수가 반환된 람다 함수를 실행한다.
- 이것을 함수 장식자로 표현할 수 있다.
@make2
@make1
def hello2():
return "Robin"
hi2 = hello2() # 함수 실행 결과를 치환
print(hi2)
- hello2 함수의 주소가 @make1에 의해 make1(hellofun)의 매개변수로 전달된다. make1 함수는 "반가워" + hello2()에 의해 수행된 "반가워 Robin"을 반환하는 람다 함수 주소를 리턴한다. 이 주소는 다시 @make2에 의해 make2(makefn)의 매개변수로 전달된다. make2 함수는 "안녕" + make1 함수 결과인 "안녕 반가워 Robin"을 반환하는 람다 함수의 주소를 리턴받아 출력된다.
· 예시2
def outer(func):
def inner(no1, no2):
print("결과: {0}".format(func(no1, no2)))
return inner # 클로저
@outer # func 함수를 감싼 구조가 됨
def func(n1, n2):
return n1 + n2
func(3, 4)
재귀 함수
· 재귀 함수(recursive function)는 수행 중인 함수 내에서 자기 자신을 다시 호출하는 패턴이다.
· 장단점: 코드의 간결함과 코딩이 편리하지만, 메모리 관리가 비효율적이다.
· 탈출 조건을 지정하지 않으면 무한루프에 빠지지 않으므로, 주의하자.
· 반복문과 재귀 함수의 차이: 재귀 함수는 함수이기 때문에 메모리 스택에 인자를 각각 따로 갖는다. 따라서 특정 상태의 인자를 특별한 코드나 변수 없이도 저장할 수 있다.
· 예시
def countdown(n):
if n == 0: # 탈출 조건
print("완료")
else:
print(n, end=' ')
countdown(n - 1) # 재귀 처리
countdown(5)
'파이썬' 카테고리의 다른 글
[Python] 파이썬과 객체 지향 프로그래밍 (3) | 2022.03.30 |
---|---|
[Python] 모듈 - 파이썬 파일의 전부 (0) | 2022.03.28 |
[Python] 파이썬 제어문: 조건문과 반복문 (0) | 2022.03.21 |
[Python] 파이썬의 변수, 연산자, 자료형, 주석 (0) | 2022.03.17 |
[Python] 파이썬의 코딩 규약 (PEP) (0) | 2022.03.15 |
댓글