[Python] List comprehension~Lambda

mandarinduk·2020년 8월 26일
1

Wecode

목록 보기
3/16

List comprehension

1.다음과 같은 도시목록의 리스트가 주어졌을때, 도시이름이 S로 시작하지 않는 도시만 리스트로 만들 때 리스트 컴프리헨션을 사용하여 함수를 작성해 보세요.

cities = ["Tokyo", "Shanghai", "Jakarta", "Seoul", "Guangzhou", "Beijing", "Karachi", "Shenzhen", "Delhi" ]

2.다음과 같은 도시, 인구수가 튜플의 리스트로 주어졌을때, 키가 도시, 값이 인구수인 딕셔너리를 딕셔너리 컴프리헨션을 사용한 함수를 작성해 보세요.

population_of_city = [(‘Tokyo', 36923000), (‘Shanghai', 34000000), (‘Jakarta', 30000000), (‘Seoul', 25514000), (‘Guangzhou', 25000000), (‘Beijing', 24900000), (‘Karachi', 24300000 ), ( ‘Shenzhen', 23300000), (‘Delhi', 21753486) ]

Iterator

이터레이터는 값을 순차적으로 꺼내올 수 있는 객체 입니다.

L이라는 다음 리스트가 있을때 for loop문으로 값마다 제곱을 하는 코드는 다음과 같습니다.

L = [1, 2, 3]
for x in L:
	print(x ** 2, end=' ')

L 리스트가 반복 가능한 객체인지 확인해보는 방법은 dir 로 호출하여 iter 함수가 있는지 확인해볼 수 있습니다.

그럼 이제 파이썬 기본함수는 중에 앞에서 알아본 iter함수와 next함수를 편하게 사용하게 해주는 iter와 next에 대하여 알아 보겠습니다. iter는 객체의 iter 함수를 호출해주고 next는 객체의 next 함수를 호출하는 함수 입니다. 위의 L 리스트에 iter와 next를 적용해서 for loop 으로 제곱한 것을 while문으로 구현해보겠습니다.

I = iter(L)
while True:
    try:
        X = next(I)
    except StopIteration:
        break
    print( X**2, end=" ")

딕셔너리도 반복가능한 객체라서 앞서본 리스트와 같이 iter함수와 next함수를 사용할 수 있고 파이썬 기본함수인 iter, next 또한 사용할 수 있습니다. 다음의 간단한 키를 출력하는 딕셔너리에 대한 for 문을 while문으로 구현해 보세요.

D = {'a':1, 'b':2, 'c':3}
for key in D.keys():
    print(key)

Generator

파이썬에서 보통의 함수는 값을 반환하고 종료 하지만 제너레이터 함수는 값을 반환하기는 하지만 산출(yield)한다는 차이점이 있습니다. 그리고 제너레이터는 쉽게 얘기하면 이터레이터를 생성해주는 함수라고도 볼 수 있습니다.

이번 과제는 다음코드를 실행해보고 분석한 결과를 블로깅하는 과제 입니다. lazy evaluation 이란 무엇인지와 장점 및 리스트 컴프리헨션과의 차이점에 대하여 블로깅 해주세요.

import time

def print_iter(iter):
    for element in iter:
        print(element)

def lazy_return(num):
    print("sleep 1s")
    time.sleep(1)
    return num

print("comprehension_list=")
comprehension_list = [ lazy_return(i) for i in L ]
print_iter(comprehension_list)

print("generator_exp=")
generator_exp = ( lazy_return(i) for i in L )
print_iter(generator_exp)

  • list comprehension은 lazy_return 함수가 모두 실행된 후에 print_iter 함수가 실행된다. 그러나 generator는 lazy_return 실행 후에 for문의 다음 cycle이 바로 실행되는 것이 아니라 외부로 빠져나와 다른 코드에게 양보한다. 그렇기에 print_iter 함수가 실행되고 그 함수 내부에서 두 번째 generator 함수가 필요하게 되자 다시 lazy_return 함수를 실행하고 다음 작업을 반복하게 된다.
  • 즉 generator 함수가 실행되는 순간에 미뤄두었던 계산을 하게 되고 이와 달리 list comprehension은 그와 관계 없이 우선 계산을 시작하고 마치게 된다.
  • 계산을 완료한 후 값을 넘겨주는 for와는 달리, generator는 계산을 하면서 중간중간 완료된 값을 넘겨준다. 이로인해 수행시간을 절약할 수 있다.

lambda

람다는 인라인 함수를 정의할 때 사용하며 익명 함수(anonymous functions) 또는 람다 표현식(lambda expression)이라고 부릅니다.

람다 표현식도 nested 될수 있고 다소 복잡한 구조를 가질 수 있지만 람다의 가장 일반적인 형식은 다음과 같습니다.

lambda argument1, argument2, ... argumentN : expression using arguments

그럼 람다의 간단한 실제 예제를 보도록 하겠습니다.

f = lambda x,y,z : x+y+z

print(f) # <function <lambda> at 0x10343e710>
print(f(1,2,3))

여기서 x,y,z 는 argument1, 2, 3 에 해당하고 expression 은 x+y+z 입니다. 따라서 간단하게 테스트해보면 function 로 출력되는 것을 확인할 수 있고 실제 함수처럼 인자를 입력해서 호출해보면 1,2,3을 더한 6이 출력되는 것을 확인할 수 있습니다. 그리고 람다는 다음과 같이 무조건 함수로 다시 작성 할 수 있습니다.

def add_func(x,y,z):
	return x+y+z

함수로 다시 작성한 함수를 보면 람다에 한줄로 return x+y+z 를 쓸 수 있을 것 같지만 람다는 표현식이므로 return을 사용할 수 없습니다.

람다는 무조건 함수로 만들 수 있는데 그럼 언제 유용하게 사용할 수 있을까요 ?

다음과 같은 간단한 인라인 콜백함수를 만들거나 함수안에서 복잡한 처리를 할 수 없을 때 유용하다고 할 수 있습니다. 콜백함수란 어떤 이벤트가 발생했을때 호출되는 함수인데 콜백함수가 여러블록으로 구성된 실행문이 아니고 다른 컴포넌트에서 사용되지 않는다면 해당 컴포넌트만을 위한 람다 표현식이 적절할 것 입니다.

다음과 같은 어떤 정수에 2제곱, 3제곰, 4제곱을 하는 예제에서 함수로 구현하는 것보다 람다로 구현하는 것이 낫겠죠?

Lambdas = [
    lambda x : x ** 2,
    lambda x : x ** 3,
    lambda x : x ** 4
]

for lambda_func in Lambdas:
    print( lambda_func(2) )

이것을 함수로 구현하면 다음과같이 구현할 수 있지만 간단함 함수임에도 함수이름을 만들어야하고 다른 함수이름과 충돌이 발생할 가능성이 있을 수 있습니다. 딱 봐도 람다 표현식을 사용한 코드가 깔끔하죠 ? 이렇게 람다는 함수가 사용되는 위치에 인라인 표현식으로 대체되는 경우 간편한 방법을 제공합니다.

def square(x): 
    return x ** 2

def power_3(x): 
    return x ** 3

def power_4(x):
    return x ** 4

powers = [ square, power_3, power_4 ]

for f in powers:
    print( f(2) )

Assignment

  1. 다음 코드를 실행해보고 print문으로 출력되는 출력결과를 확인해보고 types 모듈에 LambdaType 외에도 어떤 타입들이 있는지 조사해 보세요.
import types

f = lambda x,y,z : x+y+z

print(f)
print(type(f))
print( type(f) == types.LambdaType)

types module

  • types.FunctionType
  • types.LambdaType: 사용자 정의 함수와 lambda 표현식이 만든 함수의 형.
  • types.GeneratorType: 제너레이터 함수가 만든, 제너레이터-이터레이터 객체의 형.
  • types.CoroutineType: async def 함수가 만든 코루틴 객체의 형.
  • types.AsyncGeneratorType: 비동기 제너레이터 함수가 만든, 비동기 제너레이터-이터레이터 객체의 형.
  • types.CodeType: compile()이 반환하는 것과 같은 코드 객체의 형.
  • types.MethodType: 사용자 정의 클래스 인스턴스의 메서드 형.
  • types.BuiltinFunctionType
  • types.BuiltinMethodType: len()이나 sys.exit() 와 같은 내장 함수와 내장 클래스의 메서드의 형. (여기서, 《내장》이라는 용어는 《C로 작성된》을 의미합니다.)
  • types.WrapperDescriptorType: object.init()나 object.lt()와 같은, 일부 내장 데이터형과 베이스 클래스의 메서드의 형.
  • types.MethodWrapperType: 일부 내장 데이터형과 베이스 클래스의 연결된(bound) 메서드의 형. 예를 들어 object().str의 형입니다.
  • types.MethodDescriptorType: str.join()과 같은 일부 내장 데이터형의 메서드의 형.

(출처:파이썬 공식문서)

  1. 다음과 같이 비밀번호의 길이와 대문자가 포함된것을 확인하는 간단한 함수가 있습니다.
def check_password(password):
    if len(password) < 8:
        return 'SHORT_PASSWORD'

    if not any(c.isupper() for c in password):
        return 'NO_CAPITAL_LETTER_PASSWORD'

    return True

이함수에 있는 if문 두개를 람다표현식을 이용하여 다음과 같은 형식으로 작성해 보세요.

아래의 lambdas 리스트안에 두개의 람다표현식을 작성해야하며 주석으로 표시된 프린트가 출력결과로 나와야 합니다.

lambdas = [ 

]

def check_password_using_lambda(password):

    for f in lambdas:
        if f(password) is not None:
            return f(password)

    return True

print( check_password_using_lambda('123') )            # SHORT_PASSWORD
print( check_password_using_lambda('12356789f') )      # NO_CAPITAL_LETTER_PASSWORD
print( check_password_using_lambda('123456789fF') )    # True

profile
front-end 신입 개발자

0개의 댓글