Python

04.Function(2)

seunghyeoniya 2023. 8. 16. 09:22

-------------------------

04.Function

-------------------------

** Function

 

함수형 프로그래밍 - 함수를 매개변수로 대입해서 작업수행

 

list - 많은 양의 데이터를 저장할 때 사용

 

map(dict) - 이름으로 저장해서 누군가에게 줄 때 사용

 

필터 - filter

변환 - map

계산 - reduce

 

단일장애점 - 시스템 구성 요소 중에서 어느 하나가 동작하지 않으면 전체 시스템이 중단되는 것

 

ETL(Extract, Transform, Load) : 한 곳에 저장된 데이터를 필요에 의해 다른 곳으로 이동하는 것

-> Pre-processing(전 처리) : ETL에서 데이터의 측정 오류를 최소화하기 위해 정제, 변환, 통합 등 다양한 방법을 사용하는 것

 

1. 함수형 프로그래밍 관련 함수

1) map(function, iterable) : function에 적용시킬 함수를, iterable에 적용할 값들을 작성한다.

-> map 함수는 객체로 map을 반환하기 때문에 list나 tuple로 자료형을 변환시켜줘야 한다.

=> 데이터를 변환하기 위한 함수

=> 이 함수에 하나의 매개변수를 갖고 하나의 데이터를 리턴하는 함수를 대입하면 리턴하는 데이터들을 모아서 리턴합니다.

=> 반복문을 이용해서 직접 변환하는 것보다 속도가 훨씬 빠릅니다.

# 숫자 list를 이용하여 제곱을 가한 list를 생성

square = [i for i in range(10000)] # 0부터 9999까지의 숫자를 가지는 list를 생성

temp = []

 

# 반복문을 이용한 변환

for x in square:

temp.append(x * x)

print(temp)

 

# 함수를 이용한 변환

def f(x):

return x * x # -> 문장이 한 줄이므로 한 줄 함수인 lambda(람다)로 치환할 수 있다.

 

# square의 모든 요소에 f 함수를 적용해서 변환한 결과를 temp에 대입

temp = list(map(f, square)) # map을 이용한 변환, map -> list 자료형 변환

print(temp)

 

# 함수의 내용이 한 줄이므로 lambda(람다)로 처리

temp = list(map(lambda x : x * x, square)) # map을 이용한 변환, map -> list 자료형 변환

 

 

profile = ["SeunghyeonBan", "age", "25"]

temp1 = []

def f(x):

if len(x) > 10:

return x[0:10] + "--"

return x # -> 함수의 내용이 한 줄이 아니기 때문에 lambda(람다)로 표현할 수 없다.

 

temp1 = list(map(f, profile))

print(temp1)

 

append( ), extend( ), insert( ) 함수의 특징 : https://ooyoung.tistory.com/117

 

append 함수 : 함수이름.append(i)의 형태로 사용하며, i를 함수의 맨 끝에 객체로 추가한다.

i가 iterable한 자료형이더라도 전체를 하나의 객체로 여기고 맨 끝에 추가한다.

 

extend 함수 : 함수이름.extend(iterable)의 형태로 사용하며, iterable 데이터의 각 요소를 하나씩 함수의 맨 끝에 객체로 추가한다.

append 함수와의 차이점으로는 괄호 안에 데이터로 iterable한 자료형만 사용할 수 있다.

 

insert 함수 : 함수이름.insert(idx, i)의 형태로 사용하며, idx(원하는 위치)에 i를 객체로 추가할 수 있다.

append 함수와 마찬가지로 iterable 자료형이더라도 객체로 여기고 명령한 위치에 추가한다.

 

2) filter(function, iterator) : function에 필터링을 적용시킬 함수를, iterator에 반복 가능한 값들을 작성한다.

-> filter 함수는 객체로 filter를 반환하기 때문에 list나 tuple로 자료형을 변환시켜줘야 한다.

-> function에 iterator을 대입하여 True를 반환한 값으로만 리스트를 만들어준다.

=> 데이터의 모임에서 조건에 맞는 데이터만 골라서 데이터의 모임으로 리턴하는 함수

=> 매개변수로 하나의 데이터를 받아서 bool을 리턴하는 함수를 대입해야 합니다.

ar = ["반승현", "임한별", "신용재", "허각", None]

# 결측치 여부를 확인

print(None in ar) # ar 안에 None이 있는지 탐색

def f1(x):

return x != None

# 결측치 제거 후 list 재생성

ar = list(filter(f1, ar))

# ar = list(filter(lambda x : x!= None,ar)) # -> 한 줄이기 때문에 람다로 표현이 가능하다.

print(ar)

 

# 이름이 세 글자 이상인 데이터만 추출

def f2(x):

return len(x) >= 3 # 허각은 이름이 두 글자이기 때문에 함수에 대입하면 False를 리턴한다.

 

ar = list(filter(f2, ar))

# ar = list(filter(lambda x : len(x) >= 3,ar)) # -> 한 줄이기 때문에 람다로 표현이 가능하다.

print(ar)

 

***** iterable과 iterator

-> iterable 객체 : 반복 가능한 객체

ex) list, tuple, set, dict, str, bytes, range

 

-> iterator 객체 : 값을 차례대로 꺼낼 수 있는 객체

ex) iterable한 객체에 내장함수 iter()를 사용, iterable객체의 메서드

 

3) reduce(function, iterable) : function에 계산하는 함수를, iterable에 적용할 값들을 작성한다.

-> function에 iterable한 데이터를 왼쪽에서 오른쪽 방향으로 누적 적용하여 하나의 객체로 줄여줍니다.

=> 데이터의 모임을 가지고 연산을 수행해서 하나의 결과를 만들어내는 작업

=> 예전에는 파이썬에서 기본함수로 다루었는데 지금은 functools 패키지의 함수로 변경됨.

=> 매개변수가 2개이고 하나의 결과를 만들어내는 함수를 대입해야 합니다.

from functools import reduce

complex = reduce(lambda x, y : x*y,[1,2,3,4])

# 맨 앞의 두 객체인 1과 2의 계산결과와 세 번째 객체인 3의 계산(곱), 이 계산결과인 6을 마지막 객체인 4와 계산(곱)

print(complex)

 

4) zip (엄밀히 따지면 함수형 프로그램이 아니라, 내장 함수 중 하나이다.)

=> 여러 개의 데이터 모임을 받아서 하나의 데이터로 묶어주는 함수

=> 데이터는 tuple로 묶어줍니다.

=> 데이터의 모임에 데이터 개수가 일치해야 합니다.

key = ["초등학교", "중학교", "고등학교"]

value = ["연가초등학교", "연희중학교", "충암고등학교"]

print(list(zip(key,value)))

print(set(zip(key,value))) # set에는 순서가 존재하지 않는다.

print(dict(zip(key,value)))

 

5) High Order Function(고위함수)

=> 함수를 매개변수로 받거나 함수를 리턴하는 함수

 

6) 중첩 함수

=> 파이썬에서는 함수 내부에서 함수를 만들 수 있습니다.

=> 파이썬의 함수는 기본적으로 함수 외부에서 직접 접근하는 것은 불가능합니다.

함수 내부에서 변수를 만들면 무조건 지역 변수로 생성합니다.

=> 내부에 만든 함수는 자신을 포함한 함수의 데이터에 접근하는 것이 가능합니다.

=> 외부 함수는 내부 함수의 데이터에 접근하는 것이 안됨

=> 내부 함수는 함수 외부에서 직접 호출이 안됨

 

** => 함수 내부에서 데이터를 읽기만 할 때 자신에게서 생성이 되어 있지 않은 경우에는 외부에서 변수를 찾아서 읽기를 수행합니다.

함수 내부에서 데이터를 생성하고 내부의 데이터를 사용할 때 이 경우에는 반드시 먼저 생성하는 문장이 등장해야 합니다.

** => 함수를 만들어서 호출을 하면 먼저 함수 내부를 확인하여 함수 내부에 대입문이 있으면 대입문에 사용된 이름들을 로컬 변수로 간주를 하고,

함수를 호출할때 변수의 이름만 사용하면 로컬에 있는 변수를 호출하는 문장으로 해석하고 로컬 변수가 아닌 변수이름을 사용하면 함수 외부에서 만든 것으로 간주합니다.

 

def outer():

outer_data = "함수 외부에 만든 데이터"

def inner():

print(outer_data)

inner()

outer()

=> 위의 코드는 에러가 아님 : inner 함수를 호출할 때 outer_data는 inner 함수 외부에 있는 outer_data로 해석되어지기 때문에 에러가 발생하지 않습니다.

 

def outer():

outer_data = "함수 외부에 만든 데이터"

def inner():

print(outer_data) # -> inner함수에서는 만들지도 않은 변수를 사용했다고 인식하여 에러

outer_data = "함수 내부에 만든 데이터" # -> 만약 출력 전에 정의를 했다면, 재정의로 인식하여 에러가 아님

inner()

outer()

=> 위의 코드는 에러 : inner함수 내에서 outer_data를 정의하기 전에 사용하였다고 해석되어지기 때문에 에러가 발생합니다.

 

global : 블럭 외부에서 만들어서 모든 곳에서 사용이 가능한

local : 함수 내부에서 만들어서 함수 내부에서만 사용이 가능한

member : class 내부에서 선언하여 instance가 사용이 가능한

 

super / self / non local / global -> 상위의 블록에서도 데이터를 사용할 수 있도록 해준다.

 

깨알) 함수 호출 : 함수를 먼저 해석한 후 그 다음에 코드를 줄 단위로 실행한다.

 

7) nonlocal과 global

-> global 변수 : 일반 함수 내에서 전역 변수를 사용할 때 쓴다.

-> nonlocal 변수 : 중첩 함수 내에서 상위 함수의 변수를 사용할 때 쓴다.

=> 함수 내부에 nonlocal이나 global이라는 키워드와 함께 변수 이름을 기재하면 함수 내부에서 변수 이름을 이용하여 지역 변수를 만들 수 없게 됩니다.

nonlocal을 기재한 경우는 자신의 외부에서부터 데이터를 찾아 사용하고 global을 기재하면 최상위 레벨의 데이터를 사용합니다.

def outer():

outer_data = "함수 외부에 만든 데이터"

def inner():

nonlocal outer_data # 함수 내부에 데이터를 생성하지 않고, 외부의 데이터를 사용하기 위해서 변수를 다시 정의한다.

outer_data = "함수 내부에서 수정한 데이터" # 위의 문장에서 local -> nonlocal로 자료형을 바꾸었기 때문에, nonlocal에 변경이 적용된다.

print(outer_data)

inner()

print(outer_data)

outer()

 

=> 동일한 이름의 변수가 최상위 레벨에 존재하는 경우는 nonlocal이 아니고 global로 만들어야 합니다.

outer_data = "전역에 만든 데이터"

def outer():

def inner():

global outer_data # 함수 내부에 데이터를 생성하지 않고, 외부의 데이터를 사용하기 위해서 변수를 다시 정의한다.

outer_data = "함수 내부에서 수정한 데이터" # 위의 문장에서 local -> global로 자료형을 바꾸었기 때문에, global에 변경이 적용된다.

print(outer_data)

inner()

print(outer_data)

outer()

 

=> 동일한 이름의 변수를 여러 번 사용하면 이러한 문제가 발생할 수 있기 때문에 변수의 이름을 중첩되지 않게 잘 만들어야 한다.

과거에는 이러한 문제점을 유효범위를 표시해서 해결하였는데 로컬 변수의 이름 앞에는 "_"를, 멤버 변수의 이름 앞에는 "m_"를, 전역 변수의 이름 앞에는 "g_"를 붙이기도 했습니다.

 

8) closure

=> 함수 내부에서 만든 함수를 리턴해서 함수 외부에서 함수 내부의 데이터를 변경할 목적으로 사용

함수 안에서 만든 데이터는 함수를 호출하고 나면 소멸됩니다.

함수 내부와 외부에서 데이터를 공유하고자 하는 경우 전역 변수를 만들어서 사용할 수 있는데 구조적 프로그래밍이나 객체지향 프로그래밍에서는 전역 변수를 만드는 것을 권장하지 않습니다.

이러한 경우 closure를 이용해서 사용하는 것을 권장합니다.

def outer():

data = 0

# 자신을 감싸고 있는 함수의 데이터를 수정하는 함수

def inner():

nonlocal data

data = data + 1

print(data)

# 함수 내부에서 데이터를 수정하는 함수를 만들어 이를 리턴하는 함수를 closure이라고 합니다.

return inner

closure = outer() # 함수를 호출해서 리턴하는 함수를 변수에 저장

closure()

closure()

 

//

 

Business Logic : 실제 업무

Common Concern : 업무와 직접적인 관련이 없는 것

 

//

 

깨알) QnA. 빅데이터의 시대가 열린것은 데이터 양의 증가 때문이 아니라, 디스크의 가격이 많이 내려갔기 때문이다.

 

2. Decorator

=> 어떤 로직을 수행하기 전이나 수행한 후에 해야 할 일을 별도의 메서드로 만들어두고 @함수이름 으로 대신하도록 하는 것

=> 생성 작업이 복잡하거나 알 필요가 없는 경우 또는 business logic(업무 로직) 과 common concern(공통 관심사항 - 로깅이나 벤치마크를 위한 테스트 코드 등)을 분리하고자 하는 경우 사용합니다.

def deco(func): # ex. func = businessLogic

print("공통관심사항")

func()

# 이제부터 businessLogic 이라는 함수를 호출하면 deco 라는 함수를 수행합니다.

# deco에게 매개변수로 businessLogic 이라는 함수가 전달됩니다.

# 개발자가 작성한 코드 대신에 다른 코드를 불러내는 방식을 프록시 패턴이라고 합니다.

@deco # deco 함수를 수행시켜주세요.(at deco)

def businessLogic():

print("업무 로직")

 

businessLogic # 리턴할 것이 없어서 함수 호출할 때 괄호를 따로 쓰지 않아도 된다.

 

=> decorator를 만들 때 대부분의 경우는 함수를 리턴해서 리턴한 함수가 수행되도록 합니다.

=> decorator에 전달된 매개변수를 이용해서 함수의 이름이나 전달된 인수 그리고 리턴 값도 확인할 수 있습니다.

 

=> 함수를 호출할 때마다 실행에 걸린 시간, 인수, 리턴 값을 출력하는 decorator를 생성

import time

def clock(func):

# decorator가 적용된 함수가 호출되면 수행될 실제 함수

def clocked(*args):

start = time.time() # 현재 시간을 기록

# 업무 로직 함수를 호출

result = func(*args)

end = time.time()

elapsed = end- start # 함수의 수행시간

print("수행 시간:", elapsed)

# 매개변수 확인

print("매개변수:", args)

# 리턴 값

print("리턴값", args)

return result

return clocked

 

@clock

# 피보나치 수열을 구해주는 함수

# 첫번째와 두번째는 무조건 1

# 세번째부터는 이전 2개 항의 합

def fibonacci(n):

if n == 1 or n == 2:

return 1

else:

return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(6))

 

-> decorator를 사용하는 이유 : 복잡한 구조를 감추거나 소스의 동작에 있어서 주요 부분에 길게 덧붙이며 발생하는 에러를 방지하기 위함이다.

 

=> 표준 라이브러리에서 제공하는 데코레이터

functools 패키지에서 제공하는 lru_cache() 라는 데코레이터를 이용하면 중복된 함수 호출이 있는 경우 함수를 호출하지 않고 결과를 재사용할 수 있습니다.

lru는 무한정 캐싱되는 현상을 방지하기 위해서 오랫동안 사용하지 않은 데이터를 캐시에서 제거하는 알고리즘입니다.

ex.

위의 경우에서 사용하게 되면,

import time

def clock(func):

...

 

import functools # functools.lru_cache를 사용하기 위해서 패키지를 import 해야 한다.

@functools.lru_cache # 해당 데코레이터를 사용해서 중복되는 함수를 가져오지 않으므로 속도가 빨라진다.

@clock

def fibonacci(n):

...

 

#python

'Python' 카테고리의 다른 글

05.OOP(2)  (0) 2023.08.17
05.OOP(1)  (0) 2023.08.17
04.Function(1)  (0) 2023.08.05
03.Control Statement  (0) 2023.08.05
02.Variable_Operator  (0) 2023.08.05