상세 컨텐츠

본문 제목

[인프런 리프 2기] 10. 파이썬 중급 과정 3주차(3) 일급함수-1,2

경험/2021 인프런리프2기

by mizu-umi 2021. 3. 27. 21:44

본문

728x90

관련글 ▼

[인프런 리프 2기] 07. 파이썬 중급 과정 2주차(5) 데이터 모델-2,3

[인프런 리프 2기] 08. 파이썬 중급 과정 3주차(1) 시퀀스-1,2

[인프런 리프 2기] 09. 파이썬 중급 과정 3주차(2) 시퀀스-3,4

 


 

 

파이썬 일급함수 (1) 기본특징

  • 파이썬 함수 특징
  • 익명함수(Lambda)
  • callable 설명
  • partial 사용법

 

앞서 배웠던 것 중에 함수로 짠 프로그램은 굉장히 복잡하다고 했다. 클래스로 짜는게 훨씬 간결하다고 했는데 이번에는 매직 메소드 같은 특별한 함수들을 이용해서 프로그래밍 하는 법을 배우나보다.

 

일급함수 first class functions (일급 객체 first class object)


우선, 일급함수는 파이썬만의 개념이 아니다.

 


 

파이썬 함수 특징

  1. 런타임 초기화 - running하는 중에 초기화됨
  2. 변수를 할당할 수 있어야 한다.
  3. 함수를 다른 함수의 인수(parameter)로 전달 가능해야 한다.
  4. 함수를 결과 값으로 변환 가능해야한다(return).

일급함수를 잘 아는 것이 앞으로 배우게 될 클로저와 코루틴을 잘 이해할 수 있다.

 

함수형 프로그래밍

input이 함수의 집합을 통해 흘러간다. 각 함수는 각자의 input을 수행하고 output을 생산한다. 함수 스타일은 내부적인 상태를 수정하거나 함수의 반환값에서 보이지 않는 다른 변화를 만드는 부작용을 막는다. 아무 부작용도 없는 함수를 순순수함수(Purely functional)라고 부른다. 부작용을 피한다는 것은 프로그램이 돌아가는 동안 업데이트되는 데이터 구조를 사용하지 않는 것을 의미한다. 모든 함수의 output은 input에 의해서만 좌우되어야 한다. (출처: 파이썬 docs)

 

함수 객체

def factorial(n):
    '''Factorial Function -> n : int'''
    if n == 1 :
        return 1
    return n * factorial(n-1) # 재귀함수

class A:
    pass

n이 1일일 경우 1을 반환하라는 수행문이 있는 것은 해당 함수(factorial)에 brake를 걸기 위함이다.

 

print(factorial(5))
print(type(factorial), type(A))

위의 함수에 맞춰서 factorial을 출력하면 120이 나온다. factorial은 function으로, A는 classs로 정의된다.

 

print(dir(factorial))
print(set(sorted(dir(factorial))) - set(sorted(dir(A)))) 

factorial함수의 directory의 set에서 A클래스의 directory의 set의 교집합 부분을 빼면 factorial함수, 즉 함수가 가지고 있는 순수한 attribute만이 출력된다.

 

변수 할당

var_func = factorial # 함수만 그냥 할당한 것

print(var_func(10))
print(map(var_func, range(1,11)))

매핑(mapping)은 키(key) 역할을 하는 데이터와 값(value) 역할을 하는 데이터를 하나씩 짝지어 저장하는 데이터 구조다(출처)

 

print(list(map(var_func, range(1,11)))) # 값을 list 자료형으로 변경함

함수의 구조가 map(function, iterable) 이라서 map(var_func, range(1,11))로 입력한 것. map() 함수를 이용해 매핑을 하려면 iterable로 칠 수 있어야 하며 dir()함수를 이용해 directory를 열람했을 때 그 안에  __iter__이 있어야 한다.

 

함수를 인수로 전달하고 함수로 결과를 반환

다른 말로는 고위 함수 (Higher-Order Function)라고 한다. 여기서 map, filter, reduce 이 세가지는 파이썬 뿐 아니라 다른 언어(ex, java)에서도 사용되므로 꼭 알고 있어야 한다.

 

# ex1
print(list(map(var_func, filter(lambda x: x % 2, range(1,6)))))

# ex2
print([var_func(i) for i in range(1, 6) if i % 2])

ex1은 map, filter 등을 활용한 예제이다. ex2는 지능형 리스트이다. 결과적으로는 두 개 모두 같은 값을 출력하는데, ex1을 잘 보면 함수 안에 함수를 인수로 전달받고 있다. ex2가 가독성이 더 좋은 편이다.

 

리듀스 Reduce

from functools import reduce

reduce 함수를 사용하기 위해서는 functools에서 reduce를 import해와야한다.

 

reduce 함수의 구조 : reduce('function', 'iterable[range]') 

두개의 인자를 받아야한다.

 

from functools import reduce
from operator import add

reduce(add, range(1,11))

range(1,11)을 list로 보면 [0,1,2,3,4,5,6,7,8,9,10]인데 reduce함수를 사용하면 list안에 있는 값을 하나하나 감소(reduce)시켜가면서 add함수를 이용해 그 값들을 모두 더한다.

 

print(sum(range(1,11)))

기존의 방식이며 이게 속도가 더 빠르다.

 

익명함수

  • 일반 함수: 상기의 예제 중 factorial 같은 함수.
  • 익명 함수 : 정해진 이름이 없는 함수.

 

익명함수를 사용하게 될 때는 가급적 주석을 작성해야하며 가급적 익명함수보다 일반 함수를 작성하는 것이 좋다.

 

print(reduce(lambda x, y : x + y, range(1,11)))

앞서 add를 import하지 않고 add라는 함수를 모른다면 lambda를 이용해서 임의의 익명 함수를 만들면 된다.

 

콜러블 Callable

호출 연산자. 메소드 형태로 호출 가능한지 확인한다. call이 가능한 친구들은(?) __call__이라는 attribute를 갖는다.

 

호출이란? ( ) <- 이안에 무언가를 불러올 수 있는 자료 유형들을 의미함.

print(callable(str), callable(list), callable(factorial), callable(3.25))

여기서 유일하게 상수인 3.25만 error가 발생한다. 상수는 3.25()에서 ( ) <- 안에 호출해서 사용할 수 없기 때문이다.

 

partial 사용법

from functools import partial

reduce처럼 partial도 불러와야한다.

 

from operator import mul
from functools import partial

print(mul(10,10))

만약  mul(10,10)의 왼쪽 1을 고정시키고 다른 인수를 집어넣으려고 한다면 어떻게 하면 좋을까?

 

five = partial(mul, 5)

이런 식으로 partial을 사용하면 된다.

 

six = partial(five, 6)

참고로 고정 값을 추가하는 방법도 있다.

 

print(five(10))
print(six()) 
print([five(i) for i in range(1,11)])

print(six())에서 six()의 () 안에 값을 넣으면 오류가 발생한다.

 

왜? mul()에 호출가능한 argument의 개수가 두 개로 정해져 있는 상황에 이미 5,6을 받은 상태에서 ()에 숫자를 더 넣어버리면 argument overflow가 되어서 오류가 난다.

 


 

 

파이썬 일급함수 (2) 클로저

  • 파이썬 변수 범위(Scope)
  • Global 선언
  • 클로저 사용이유
  • class -> Closure 구현 (class형태로 closure구현)

 

앞서 선생님이 Closure를 class형태로 구현할 것이라고 말했음에도 불구하고 내용이 아리송다리송해서 영상을 여러번 다시 돌려봤다. 강의 중 하신 말씀처럼 클로저에 대한 개념을 이해하려면 다른 문서들도 많이 봐야할 것 같다.

 

개발적으로 어려운 분야는 어딜까?

웹 프로그래밍이나 일반적인 응용 프로그램에서는 동시성이다. 하나의 공간에 여러개가 들어왔을 때 발생할 수 있는 교착상태나 경쟁상태(race condition)를 의미하며 클로저는 그런 걸 도와준다(?)

 


파이썬 변수 범위 Scope

# Ex1
def func_v1(a):
    print(a)
    print(b)

func_v1(10)

b는 특정한 값이 선언된 변수가 아니므로 무시된다.

 

# Ex2
b = 20 # 함수 밖에 있는 것은 global

def func_v2(a): # 함수 안에 있는 것은 local
    print(a)
    print(b)

func_v2(10)

여기서는 global 영역에 있는 b라는 변수에 20이라는 값이 할당되었다. a라는 local 변수는 함수에 10을 호출함으로써 각각 10과 20이 출력된다.

 

# Ex3
c = 30

def func_v3(a):
    print(a)
    print(c)
    c = 40
    
func_v3(10)

여기서는 UnboundLocalError: local variable 'c' referenced before assignment 라는 error가 발생한다. local에 있는 c라는 변수가 값이 할당되기도 전에 참조되었다는 오류이다.

 

global의 c를 출력하려면

c = 30

def func_v3(a):
    global c
    print(a)
    print(c)
    c = 40
    
func_v3(10)

global c를 선언해주면 된다.

 

함수는 어떤 동작이 실행될 것인지를 정의해두는 것이다. 따라서 해당 동작을 실행시킬 수 있는 문장이 존재하지 않으면 실행되지 않으며 실행하려는 함수보다 앞서 작성된 수행문이 있다면 그것을 우선시 한다. 

 

print('>>', c)
func_v3(10)
print('>>>', c)

위의 예제는 print가 함수보다 먼저 실행되었기 때문에 우선 함수를 배제하고 생각해야한다. global의 c의 값인 30이 먼저 출력되고 함수가 실행되는 순간에는 global c가 동작하기 때문에 c의 값이 30에서 40으로 재할당(re-assigned)된다.

 

클로저 Closure 사용이유

서버 프로그래밍에서 동시성을 제어하는 게 매우 중요하다.

 

  • 동시성(CONCURRENCY) : 메모리 공간에 여러 자원이 한번에 접근해서 교착상태(dead lock)이거나 경쟁상태(race condition)에 빠지는 경우

 

결론: 클로저는 불변 상태를 기록한다.

 

a = 100

print(a + 100)
print(a + 1000)

이 경우 a의 값은 고정되어 있기 때문에 누적된 값이 나오는 건 아니다.

 

# 결과 누적 (함수)
print(sum(range(1,51)))

알아서 누적해서 값을 구해준다.

 

클래스를 사용해서 클로저를 구현하자

class Averager():
    def __init__(self):
        self._series = [] 

    def __call__(self, v): 
        self._series.append(v)
        print('inner >> {} / {}'.format(self._series, len(self._series)))
        return sum(self._series) / len(self._series)

[] 빈 리스트 안에 호출되는 값을 입력하므로써 값이 누적된다.

 

# instance
averager_cls = Averager()

#누적
print(averager_cls(10))
print(averager_cls(30))

누적 값은 처음에는 [10] 이었다가 30이 호출되면 [10, 30]으로 늘어난다.

 


< 강의 출처 - 인프런 우리를 위한 프로그래밍 파이썬 중급 (Inflearn Original) 

728x90
반응형

관련글 더보기

댓글 영역