21.1.24

<코딩도장 복습 통한 새로 이해한 내용 정리 : Unit : 32~33>

  1. 람다 표현식 :
    "함수를 다른 함수의 인수로 넣을 때 주로 사용"
    -> 자바스크립트에서 함수는 객체이기 때문에 다른 함수의 인자가 될 수 있다는 이야길 들은 것 같다. 그 원리를 이용한 객체지향적 개념이 아닐까 싶다.
def plus_ten(x):
	return x+10

이를 람다 표현식으로 하면 다음과 같다

plus_ten = lambda x : x + 10

즉 "함수 이름 = lambda 매개변수 : 식"

따라서 람다 식 전체를 괄호 친 후 (인자) 를 넣으면 바로 실행되기도 한다. 단 이미 만들어진 람다의 식 안에 또 다른 매개변수가 들어갈 수는 없다.(따로 선언해야 함.)

  • 단 코드에 길이가 너무 길어지겠다 싶으면 그냥 함수 선언을 하는 것이 더 좋을 때가 있다.

  • list처럼 시퀀스 객체를 인자로 받아야 하는 상황에서는 list(map...)을, 리턴 값이 어떤 필터링이 필요하다면 filter를 메서드로 사용한다.

  • reduce함수 : 반복 가능한 객체(시퀀스 객체)의 누적 값을 반환하는 함수로 내장함수가 아니어서 불러와야 함. 사용법 및 결과는 다음과 같음

>>> def f(x, y):
...     return x + y
...
>>> a = [1, 2, 3, 4, 5]
>>> from functools import reduce
>>> reduce(f, a)
15

(람다)
>>> a = [1, 2, 3, 4, 5]
>>> from functools import reduce
>>> reduce(lambda x, y: x + y, a)
15
  • 문자열 포메팅과 메서드를 이용한 연습문제32
files = ['font', '1.png', '10.jpg', '11.gif', '2.jpg', '3.png', 'table.xslx', 'spec.docx']
print(list(filter(lambda x : x.find('.jpg') != -1 or x.find('.png')!= -1, files)))

풀이 : 먼저 map을 사용할 경우 리스트로 만들어지기는 하지만 분류가 되지 않는다. 그러므로 filter를 사용한다.
다음 단순히 문자열 포멧팅을 사용할 경우 모두가 jpg, png로 바뀌는 알고리즘만 나온다. 따라서 문자열 메서드 중 find의 성질을 이용. 특정 값이 나오면 몇 번 째 인덱스인지 찾아주고 없으면 -1을 출력하는 성질을 반대로 이용. 생각이 안 나서 한참을 고민했다가 풀었다. 아마 푼지 좀 되어서 방식을 다시 까먹은 것 같다.

  1. 클로저
  • 변수의 사용 범위 :

스크립트 전체에서 사용할 수 있는 변수 : 전역변수
그 범위 : 전역범위

x = 10          # 전역 변수
def foo():
    print(x)    # 전역 변수 출력
 
foo()
print(x)        # 전역 변수 출력

반대로 특정 함수 혹은 특정 지역에서만 사용하는 변수를 지역변수. 그 범위를 지역범위라고 함.

    x = 10      # foo의 지역 변수
    print(x)    # foo의 지역 변수 출력
 
foo()
print(x)        # 에러. foo의 지역 변수는 출력할 수 없음

만일 전역 변수가 있는데 이를 '지역변수로만' 사용하고 싶다면?
global 선언 통해 지역변수 자리에서 전역변수를 선언하여 전체 사용.

x = 10          # 전역 변수
def foo():
    global x    # 전역 변수 x를 사용하겠다고 설정
    x = 20      # x는 전역 변수
    print(x)    # 전역 변수 출력
 
foo()
print(x)        # 전역 변수 출력
  • 함수 안에서 함수를 만든다.
def print_hello():
    hello = 'Hello, world!' # 함수 정의
    def print_message():
        print(hello) # 함수 정의
    print_message() # 함수 호출
 -----(지역변수 hello접근 범위)------
print_hello() # 함수 호출

다른 예시

def A():
    x = 10        # A의 지역 변수 x
    def B():
        x = 20    # x에 20 할당
 
    B()
    print(x)      # A의 지역 변수 x 출력
 -------(지역변수 x = 10 접근 범위)-------
A()
10

A함수의 지역변수가 작용하는 범위라 10이 나옴.

만일 20이 나오게 하고 싶다면? -> nonlocal x

def A():
    x = 10        # A의 지역 변수 x
    def B():
        nonlocal x    # 현재 함수의 바깥쪽에 있는 지역 변수 사용
        x = 20        # A의 지역 변수 x에 20 할당
 
    B()
    print(x)      # A의 지역 변수 x 출력
 
A()
20 (바깥쪽 쓰는 것으로 변경)

하지만 실무에서 이렇게 쓰는 일 별로 없음. 변수는 이름 꼭 다르게 사용할 것을 추천.

이러한 것들을 상관 안 하고 global을 쓰면 무조건 전역변수를 쓸 수 있다.

x = 1
def A():
    x = 10
    def B():
        x = 20
        def C():
            global x
            x = x + 30
            print(x)
        C()
    B()
 
A()
31
  • 본격 클로저 알아보기
def calc():
    a = 3
    b = 5
    def mul_add(x):
        return a * x + b    # 함수 바깥쪽에 있는 지역 변수 a, b를 사용하여 계산
    return mul_add          # mul_add 함수를 반환
 
c = calc()
print(c(1), c(2), c(3), c(4), c(5))

calc()는 호출되는 것으로 사실 함수가 끝났는데 c(1), c(2).. 처럼 지역 변수, 코드를 유지하고 있다가 호출 할 때 안에 가지고 있는 함수를 다시 사용하는 개념을 '클로저'라고 함.

클로저는 프로그램의 흐름(함수 형태 등)을 변수에(위의 c처럼) 저장하여 쓰고 싶을 때 쓸 수 있다. 클로저에 지역 변수는 바깥에서 직접 법근이 어렵고 데이터를 숨기고 싶을 때 주로 사용한다.
여기서 두 번째로 def 된 함수를 람다식으로도 표현할 수 있다고 한다.
(return lambda x: a * x + b) : 함수 안에 함수를 넣은 형태

클로저 : 변수라는 이름을 가진 환경을 유지했다 쓰는 함수
람다 : 이름이 없음.(변수 등을 지정하지 않는다는 뜻)

만일 지역변수에 변화를 주고 싶다면(이를테면 누적식을 만들어버리고 싶다.) nonlocal을 사용하면 된다.

def calc():
    a = 3
    b = 5
    total = 0
    def mul_add(x):
        nonlocal total
        total = total + a * x + b
        print(total)
    return mul_add
 
c = calc()
c(1)
c(2)
c(3)

개념 정리 후 드는 생각 :
한 6~7번은 보니까 쪼금은 알 것 같다. 먼저 지역변수와 전역변수의 차이, 지역 범위와 전역 범위의 차이부터 잘 익혀두자.

  • 개념 응용 연습문제 32
def counter():
    i = 0
    def count():
      nonlocal i
      i += 1
      return i
    return count
c = counter()
for i in range(10):
    print(c(), end=' ')

nonlocal로 counter의 변수 i를 가지고 온 뒤 counter가 호출 될 때마다 1 씩 느는 것이므로 +=1 처리를 해준다. 이후 이걸 그냥 리턴해주면 된다.(print처리 하니까 counter의 nonetype 값까지 같이 나와서 애먹었다.)

좋은 웹페이지 즐겨찾기