파이썬에서 함수 = 일급 시민(first class citizen)
일급 시민 (first class citizen)
: 변수에 대입 가능
: Argument로 사용 가능
: 함수나 메소드의 반환값으로 사용 가능
지역변수는 해당 함수 내에서만 사용 가능한 변수이다.
따라서 아래와 같이 코드를 작성 할 경우 실행시 오류가 나게 된다.
a = 10
b+20
def 가():
print(a+ b)
def 나():
print(a*b)
print(c)
def 다():
c = 30
print(c)
이를 해결하기 위해서는 return 을 사용하여 함수 다()에서 사용했던 c를 나()에서도 사용 할 수 있도록 만들어야 한다. 따라서
a = 10
b+20
def 가():
print(a+ b)
def 나():
print(a*b)
r = 다()
print(c)
def 다():
c = 30
print(c)
return c
이렇게 코드를 바꾸면 실행이 가능해 진다.
또한 여기에서도 전역 변수인 가() 함수나 나() 함수를 호출할 수 있다.
def 다():
나()
def 라(a):
a += 8
print(a)
a += 8
print(a)
a += 8
print(a)
그러나 이러한 코드를 수정할 경우 하나만 놓치거나 잘못 수정하게 되면 에러가 발생하게 된다. 따라서 이러한 불푠함을 해결하기 위해 함수 안에 함수를 집어넣어서 사용한다.
def 라(a): # ouyrt function
def 마():
print(a+10) #inner function
마()
마()
마()
마()
따라서 이처럼 마() 라는 함수를 라() 함수 안에 추가해서 코드를 수정 할 때 하나만 변경해서 일괄적으로 변경 가능하도록 한다. 그리고 이때, 마() 부분은 함수를 정의한 것으로 여겨진다. 그리고 여기서 라()는 outerfuntion으로 마()는 innerfunction이 된다.
지역함수 local function
= 중첩 함수(Nested function): 함수 안에 정의 한 함수
outer function = 지역함수가 선언된 함수
inner function = 지역함수inner function은 outer function의 지역변수를 자유롭게 사용 가능
- 기본적으로 inner function은 outer function 안에서만 호출 가능
- 그러나 outer function이 정의된 inner function을 return value로 반환하면 밖에서도 호출 가능
안쪽의 함수는 바깥 쪽 함수의 지역 변수에 접근 가능
변수에는 global(전역) 변수와 local(지역) 변수가 존재한다. 그리고 그러한 변수의 사용범위(scope)를 보면 그 변수가 선언된 영역(block) 내에서 호출 가능하고, 또한 그 하위 영역(block)에서도 호출(사용) 가능하다.
global_var = "Global 전역변수"
print(1, global_var)
def outer():
local_var = "Outer 함수의 Local (지역) 변수"
print(2, global_var)
print(3, local_var)
outer()
# print(local_var)
위의 코드를 보면, 여기에는 globlar_var 이라는 함수 안에 outer 라는 변수가 존재한다. 따라서 outer 변수가 속해있는 변수인 global 변수도print(2, global_var)
라고 사용이 가능하다. 이때 outer 변수 부분에서는 함수를 호출은 안하고 정의만 한다. 그러므로 이 안의 것을을 일을 시키기 위해서는 호출해야한다. 만약 맨 아래의 # print(local_var)
처럼 작성할 경우, locla_var은 innerfunction이지만 바깥 block(outerfuncion)의 위치에 속해지기 때문에 사용이 불가하게 된다.
그리고 이러한 함수 구조에서 딱히 좋은 것은 아니지만, global변수의 값을 변경할 경우에는 선언을 따로 해주어야 하는데, outer 함수에서 global 변수의 값을 변경 할 경우 global 변수명
을 사용하여 선언을 하고, inner 함수에서 outer 함수의 local 변수를 변경 할 경우에는 nonlocal 변수명
을 사용하여 선언을 해준다.
ex)
global_var = "Global 전역변수"
print(1, global_var)
def outer():
global global_var
local_var = "Outer 함수의 Local (지역) 변수"
print(2, global_var)
print(3, local_var)
global_var = "Outer 에서 변경한 global_var의 값"
print(global_var)
def inner():
nonlocal local_var
inner_local_var = "Inner 함수의 Local (지역) 변수"
print(4, global_var)
print(5, local_var)
local_var = "Inner에서 변경한 local_var의 값"
print("5-2", local_var)
print(6, inner_local_var)
# print(inner_local_var)
inner()
print("5-3", local_var)
outer()
# print(local_var)
그리고 위의 코드에서 # print(inner_local_var)
로 표기한 부분을 보면, inner() 함수에서 정의한 지역 변수는 outer 함수에서 호출이 안되어 실행할 경우에 error가 나온다. 그리고 inner 함수 또한 호출 되어야 이릉ㄹ 하기 때문에 inner()
로 호출했음을 볼 수 있다. 또한. 코드 맨 아래 부분에 위치한 # print(local_var)
을 보면, inner함수인 local_var은 바깥의 block에서는 사용할 수 없음을 볼 수 있다. 따라서 이를 실행시킨다면 error가 발생하게 된다.
closure
: 파이썬 실행환경이 inner function이 종료될때까지 outer function의 지역변수들(parameter포함)을 사용할 수 있도록 저장하는 공간
따라서 clousre을 inner함수에서 outer 함수에 있는 지역변수를 건드림으로서 inner함수의 생명을 더 길어지게 하는 것이라고 이해해도 된다.
ex)
def outer():
outer_var = 10
def inner():
print(outer_var) #inner 함수에서 outer 함수의 변수를 호출
return inner # inner 함수를 반환
여기서 outre_var은 outer 함수의 지역변수이다. 그리고 inner함수에서 print를 통해 outer 함수의 변수를 호출을 한다 그리고 이 결과로 아래의 코드를 보면
f = outer()
f()
여기서 f= outer() 가 f = inner함수를 사용한 것과 같다고 볼 수 있다. 따라서 실행을 해보면, 이 결과로 10이 나오는 것을 볼 수 있다.
- 기존의 함수를 수정하지 않고 그 함수 전/후에 실행되는 구문을 추가할 수 있도록 하는 함수
- 기존 함수 코드를 수정하지 않고 새로운 기능의 추가를 쉽게 해줌
- 추가 기능을 다수의 함수에 적용 가능
- 함수의 전/후 처리 하는 구문을 필요하면 붙이고 필요 없으면 쉽게 제거 가능
def a():
print("안녕")
def b():
print("="*50)
print("Hello")
print("="*50)
이런 함수가 있을 때
a()
a()
b()
a()
위의 코드를 실행한다면
이러한 결과를 얻을 것이다. 그런데 이때 '='라는 기호 대신 다른 기호로 바꾸고자 하였을 때 다시 해당 코드 부분으로 가서 하나씩 수정해야 한다는 불편함이 있다. 또한 해당 코드가 내가 아닌 타인이 만든 것이나 내장함수라면 일이 더 복잡해진다. 따라서 이러한 경우에서 기존의 코드에다가 함수 수정 없이 다양한 것을 추가 할 때, decorator를 사용한다.
아래의 그림을 보면 기존의 def_origial에서 def_original의 앞, 뒤로 새로 추가를 하게 된다.
ex)
위에서 사용했던 코드를 다시 사용해서 살펴보자.
def a():
print("안녕")
def b():
print("="*50)
print("Hello")
print("="*50)
이러한 코드가 있다고 했을 때 이 함수를 이용해서 아래 코드를 작성 할 수 있다.
def equal_deco(func):
def wrapper(): #이런 함수를 하나 만든다
print("="*30) # 전처리 작업
func() #함수 호출 -> 원래 함수의 작업
print("="*50) # 후처리 작업
return wrapper # 정의한 함수를 return 시킨다
#inner함수는 끝났지만 return되어 아래의 결과를 보면 알 수 있듯이 살아살아서 도출된다
위의 코드에서 parameter은 original 기능을 실행하는 함수로 함수를 받는 역할을 한다. 그리고 inner 함수는 origianl함수의 호출을 처리하며, 위 코드에서 "===" 와 같은 함수 호출 전후로 해야 할 것이 있으면 그 처리를 담당한다.
그리고 equal_deco라는 함수 내에 wrapper 라는 함수를 하나 만든다. 이 함수 내에는 위의 def_original 그림에서 볼 수 있듯이 기존의 함수 위 아래로 전처리 작업과 후처리 작업을 지시한다. 이후 정의를 완료한 함수를 다시 return 시킴으로서 inner 함수의 내용이 이후에도 읽힐 수 있도록 한다.
따라서 이러한 과정 이후에는 아래와 같은 코드를 작성 할 수 있다.
# a()
a_f = equal_deco(a)
a_f()
이때 여기서 a_f()라는 실행된(호출한) 함수 는 inner 함수에 해당하던 wrapper()이다.
그리고 추가 없이 원래의 함수를 호출하고 싶다면 다시 original 함수인 a()
라는 함수를 실행하면 된다.
b_f = equal_deco(b)
b_f()
그리고 이처럼 이러한 구조는 어떤 함수에도 적용 할 수 있다.
또한, 여기서 =
대신 #
을 사용하고 싶다면 아래의 코드처럼 사용하면 된다.
def sharp_deco(func):
def wrapper():
print("#"*30)
func()
print("#"*20)
return wrapper
사용 후 위의 과정을 그대로 적용하면 =
대신 #
가 적용 된 것을 볼 수 있다.
그러나 이 과정에서 조합을 내가 하는것에 불편함을 느낄 수 있다. 그래서그러한 불편함을 해결하기 위해서 @equal_deco
를 앞에 붙여 decorator 를 original 함수를 정의 시 추가하라고 선언을 한다.
@equal_deco
def a2():
print("안념")
@sharp_deco
def a2():
print("안념")
따라서 코드를 이러한 방식으로 작성하면 위와 같은 결과를 얻을 수 있다. 그리고 위의 두 코드에서 @
를 생략한다면 기존의 original 함수가 호출된다.
- 전/후처리 기능을 추가할 함수를 parameter로 받는다.
- 그 함수 호출 전후로 추가할 기능을 작성한 지역함수를 정의한다.
2번의 함수를 반환한다.def decorator(func): def wrapper([parameter]): # decorator 적용할 함수에 파라미터를 전달할 경우 parameter 변수들을 선언 # 전처리 func() # 후처리 return wrapper
⇒ @decorator이름
를 적용하고자하는 함수 선언전에 기술
@decorator
def caller([parameter]):
...
ex)
def sharp_deco2(func):
def wrapper(param):
print("="*50)
func(param)
print("="*50)
return wrapper
위의 코드를 실행하고 이어서 아래 코드를 실행 할 때, wrapper함수는 parameter를 받지 않는다. 따라서 해당 코드 앞에 decorator가 붙을 경우 error가 나오게 된다. 따라서@sharp_deco
은 아래 코드의 앞에 올 수 없다.
def greet(name):
print(f"{name}님 환영합니다")
greet("홍길동")
그러나 이 코드의 앞에 @
를 붙이고 싶다면, 이렇게 고쳐주면 된다.
@sharp_deco2
def greet(name):
print(f"{name}님 환영합니다")
return f"인삿말-{name}"
그 뒤에 이러한 코드를 실행하면 wrapper()는 return을 하지 않았기 때문에 none이라는 결과가 나오게 된다.
v = greet("홍길동")
print(v)
따라서 아래처럼 return result
를 추가하여 wrapper()을 return시키고 다시 코드를 실행시키면 none 대신 아래와 같은 결과가 도출된다.
def sharp_deco2(func):
def wrapper(param):
print("="*50)
result = func(param)
print("="*50)
return result
return wrapper
@sharp_deco2
def greet(name):
print(f"{name}님 환영합니다")
return f"인삿말-{name}"
v = greet("홍길동")
print(v)
그리고 이 코드에서 글자의 길이를 판단하거나 결과 앞에 공백을 입력하는 등의 변화를 줄 수 있다.
unique time : 1970년 1월 1일 0시 0분 0초
Q. 1970년 1월 1일 0시 0분 0초부터 time.time() 함수가 실행 된 시점까지 몆초 지났는지를 반환
timestamp : 1970년 1월 1일 0시 0분 0초부터 얼마 지났는지로 현재 시간을 관리
import time
v = time.time()
print(v)
v = time.time()
print(v)
time.sleep()은 설정된 초만큼 실행을 멈추게 하는 것으로 sleep(초)
형태로 사용하며, 타이머 같은 기능이라고 볼 수 있다.
time.sleep(2) # 2초를 멈춘다
ex)
s = time.time()
for _ in range(3):
print("a")
time.sleep(3)
e = time.time()
print(e-s, "초")
ex)
import time
# 함수가 실행하는데 걸린 시간을 체크하는 decorator정의
def timechecker(func):
def wrapper():
s = time.time()
v = func()
e = time.time()
print(f"{e-s}초")
return v
return wrapper
@timechecker
def func1():
print(1)
time.sleep(2)
print(2)
@timechecker
def func2():
print(1)
time.sleep(4)
print(2)
@timechecker
def func3():
print(1)
time.sleep(1)
print(2)
위 과정 후 아래 코드를 실행하면
func1()
func2()
func3()
이러한 결과를 보여준다.
- 반올림 (내장함수)
round(2.008725643157959)
round(4.0135650634765624, 5) #5: 자리수 - 소수점 5자리 이하에서 반올림
round(199.999, -1)0: 소숫점 이하에서 반올림
양수: 소수점 위치
음수: 정수부 위치
.nf
형태로 사용
: 소수점 n번째 자리 이하에서 반올림
→ f : flot 타입
ex)
a = 2.1723940523457893
f"{a:.2f}초"
output: 2.17초
def test():
pass
test.__name__
# 함수의 이름을 조회 가능
import time
def timechecker(func):
def wrapper(*args, **kwargs): #가변인자가 받은 것을 묶어주기
s = time.time()
v = func(*args, **kwargs) #그걸 다시 원소단위로 풀기
e = time.time()
print(f"{func.__name__}걸린시간: {e-s:.2f}초")
return v
return wrapper
기존의 코드에서 위와 같이
print(f"{func.__name__}걸린시간: {e-s:.2f}초")
이렇게 추가해서 다시 위와 같은 과정을 거치면
이러한 결과를 보여준다.