[Advanced Python] -1

김가람휘·2022년 2월 7일
0

Python

목록 보기
6/14

1. list comprehensions

  • 리스트 컴프리헨션 : 리스트를 쉽게, 짧게 한 줄로 만들 수 있는 파이썬 문법
  • [ 표현식 for 원소 in 반복가능한객체 ]
  • [ 표현식 for 원소 in 반복가능한객체 if문 ]
new_list = [x for x in range(1,11)]
print(new_list) # [1,2,3,4,5,6,7,8,9,10]
list_comprehension = [element for element in range(1,11) if (element % 2) == 1]
print(list_comprehension) # [1,3,5,7,9]
  1. 다음과 같은 도시목록의 리스트가 주어졌을때, 도시이름이 S로 시작하지 않는 도시만 리스트로 만들 때 리스트 컴프리헨션을 사용하여 함수를 작성해 보세요.
cities = ["Tokyo","Shanghai","Jakarta","Seoul","Guangzhou","Beijing","Karachi","Shenzhen","Delhi"]

def list_comprehension():
    city_list = [city for city in cities if (city[0] != "S")]
    print(city_list)

list_comprehension() 

# ['Tokyo', 'Jakarta', 'Guangzhou', 'Beijing', 'Karachi', 'Delhi']
  1. 다음과 같은 도시, 인구수가 튜플의 리스트로 주어졌을때, 키가 도시, 값이 인구수인 딕셔너리를 딕셔너리 컴프리헨션을 사용한 함수를 작성해 보세요.
population_of_city = [ ("Tokyo", 36923000), ("Shanghai", 34000000), ("Jakarta", 30000000), ("Seoul", 25514000), ("Guangzhou", 25000000), ("Beijing", 24900000), ("Karachi", 24300000), ("Shenzhen", 23300000), ("Delhi", 21753486) ]

def list_comprehension2():
    population_of_city_list = {city:population for city, population in population_of_city}
    print(population_of_city_list)

list_comprehension2()

# {'Tokyo': 36923000, 'Shanghai': 34000000, 'Jakarta': 30000000, 'Seoul': 25514000, 'Guangzhou': 25000000, 'Beijing': 24900000, 'Karachi': 24300000, 'Shenzhen': 23300000, 'Delhi': 21753486}

2. iterators

  • 이터레이터 : 값을 순차적으로 꺼내올 수 있는 객체
  • iterable : 반복 가능한 객체 (list, dict, set, str, bytes, tuple, range)
L = [1,2,3] # iterable

iterator_L = L.__iter__()
print(L.__iter__())
# <list_iterator object at 0x10a999210>
print("dir iterator_L = ", end=""), print(dir(iterator_L))
# dir : 어떤 객체를 인자로 넣어주면 해당 객체가 어떤 변수와 메소드를 가지고 있는지 나열해줍니다.
# dir iterator_L = ['__class__', '__delattr__', '__iter__', ......]

print(iterator_L.__next__()) # 1
print(iterator_L.__next__()) # 2
print(iterator_L.__next__()) # 3
print(iterator_L.__next__()) # StopIteration
L = [1,2,3,4]
I = iter(L) # I = L.__iter__()

while True:
	try:
    	X = next(I) # I.__next__()
    except StopIteration:
    	break
    print(X**2, end=" ") # 1 4 9 16
    
# 다음의 간단한 키를 출력하는 딕셔너리에 대한 while문을 구현해 보세요.    
D = {'a':1, 'b':2, 'c':3}
I2 = iter(D)

while True:
	try:
    	key = next(I2)
    except StopIteration:
    	break
    print(key, end=" ") # a b c

3. generators

  • 제너레이터 : 배열이나 리스트와 같은 반복가능한 연속적인 값들을 생성해 내는 패턴
def generator_squares():
	for i in range(3):
    	yield i**2
        
print("gen object=", end=""), print(generator_squares())
# gen object=<generator object generator_squares at 0x10f3b0150>
print("dir gen =", end=""), print(dir(generator_squares()))
# dir gen =[....., '__iter__',..., '__next__',....]

gen = generator_squares()
print(gen.__next__()) # 0
print(gen.__next__()) # 1
print(gen.__next__()) # 4
print(gen.__next__()) # StopIteration

# 제너레이터에서는 이처럼 yield를 이용해서 제너레이터 함수 실행중 값을 전달 할 수 있고, 
# 응용하면 제너레이터 함수를 사용해서 main 실행 루프에서 연산결과에 따라 호출도 제어할 수 있다.
def generator_send():
	received_value = 0
   	
    while True:
    	# received_value는 일종의 placeholder
    	received_value = yield # 언젠가 들어올 값을 미리 placeholder에 박아둠
        print("received_value = ", end="", print(received_value)
        yield received_value * 2
        
gen = generator_send()
next(gen) # 실제로 placeholder로 박아둠
print(gen.send(2)) # 제너레이터로 값을 보냄

next(gen)
print(gen.send(3))

# received_value = 2
# 4
# received_value = 3
# 6
  • 제너레이터 표현식의 문법은 리스트 컴프리헨션 문법과 비슷하지만 대괄호([])가 아닌 괄호()를 사용하여 만듭니다.
L = [1,2,3]

def generate_square_from_list():
	result = (x*x for x in L)
    print(result)
    return result
    
def print_iter(iter)
	for element in iter:
    	print(element)
        
print_iter(generate_square_from_list())

# <generator object generate_square_from_list.<locals>.<genexpr> at 0x0000019208911FC0>
# 1
# 4
# 9
  1. 다음코드를 실행해보고 분석한 결과를 블로깅하는 과제 입니다. 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)

# comprehension_list=
# sleep 1s
# sleep 1s
# sleep 1s
# 1
# 2
# 3
# generator_exp=
# sleep 1s
# 1
# sleep 1s
# 2
# sleep 1s
# 3
  • Lazy evaluation
    • 어떠한 값이 실제로 쓰일 때까지 그 값의 계산을 뒤로 미루는 것
    • 어떤 값이 필요할 때에만 연산을 수행하는 것은 컴퓨팅 자원의 절약에 도움이 된다.
    • 전체 연산의 시간을 줄일 수 있다.
    • 코드가 점점 복잡해지고 난해해 질 수 있다.
    • 리스트 컴프리헨션 : lazy_return을 3회 연속 실행한 후 print_iter을 1회 실행
    • 레이지 에볼루션 : lazy_return 1회 실행 후 print_iter을 1회 실행하는 과정을 3회 반복
import time
import random

counter = random.randrange(1, 11)  # 1부터 10사이의 랜덤 값 생성
print("counter: {}".format(counter))

def return_one_after_five_sec():
    print("please wait for 5 seconds")
    time.sleep(5)
    print("return 1")
    return 1

print("[let's make one_list !]")
one_list = [return_one_after_five_sec() for x in range(10)] # list_comprehensions 생성

# counter 숫자만큼 값 출력
print("[let's print one_list !]")
for item in one_list:
    counter -= 1
    print(item)
    if counter == 0:
        break
        
# counter: 2
# [let's make one_list !]
# please wait for 5 seconds
# return 1
# please wait for 5 seconds
# return 1
# please wait for 5 seconds
# return 1
# please wait for 5 seconds
# return 1
# please wait for 5 seconds
# return 1
# please wait for 5 seconds
# return 1
# please wait for 5 seconds
# return 1
# please wait for 5 seconds
# return 1
# please wait for 5 seconds
# return 1
# please wait for 5 seconds
# return 1
# [let's print one_list !]
# 1
# 1
  • 50초에 걸쳐 리스트를 미리 다 만들어 놓았으나 랜덤한 counter 값이 2가 나와 실제로 쓰인 값은 두 개이다.
    -> 이런 상황에서는 generator로 만들어놓는게 효율적이다! (대괄호만 소괄호로 바꾸면 된다.)
import time
import random

counter = random.randrange(1, 11)  # 1부터 10사이의 랜덤 값 생성
print("counter: {}".format(counter))

def return_one_after_five_sec():
    print("please wait for 5 seconds")
    time.sleep(5)
    print("return 1")
    return 1

print("[let's make one_generator !]")
one_generator = (return_one_after_five_sec() for x in range(10))  # generator 생성

# counter 숫자만큼 값 출력
print("[let's print one_generator !]")
for item in one_generator:
    counter -= 1
    print(item)
    if counter == 0:
        break
        
# counter: 2
# [let's make one_generator !]
# [let's print one_generator !]
# please wait for 5 seconds
# return 1
# 1
# please wait for 5 seconds
# return 1
# 1
  • 대부분의 요소가 사용될 것이 확실하다면 list를 통해 미리 연산을 하는 것이 효율적이지만 그렇지 않으면 generator사용하는 것이 더 효율적이다.

4. Lambda expressions

  • 람다 : 인라인 함수를 정의할 때 사용하며 익명 합수(anonymous functions) 또는 람다 표현식(lambda expression)이라고 부릅니다.
  • 필요한 곳에서 즉시 사용하고 버릴 수 있는 일시적인 함수
  • 람다는 표현식이므로 return을 사용할 수 없습니다.
  • 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)) # 6
Lambdas = [
    lambda x : x ** 2,
    lambda x : x ** 3,
    lambda x : x ** 4
]
for lambda_func in Lambdas:
    print(lambda_func(2))
# 4
# 8
# 16
  1. 다음 코드를 실행해보고 print문으로 출력되는 출력결과를 확인해보고 types 모듈에 LambdaType 외에도 어떤 타입들이 있는지 조사해 보세요.
import types

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

print(f) # <function <lambda> at 0x0000021E6D62AB00>
print(type(f)) # <class 'function'>
print( type(f) == types.LambdaType) # True

types의 종류

  • types.FunctionType : 사용자 정의 함수
  • types.LambdaType : lambda 표현식이 만든 함수
  • types.GeneratorType : 제너레이터 객체의 형
  • types.CoroutineType : 코루틴 객체의 형
  • types.AsyncGeneratorType : 비동기 제너레이터 함수가 만든 비동기 제너레이터-이터레이터 객체의 형
  • types.CodeType : compile()이 반환하는 것과 같은 코드 객체의 형
  • types.MethodType : 사용자 정의 클래스 인스턴스의 메서드 형
  1. 다음과 같이 비밀번호의 길이와 대문자가 포함된것을 확인하는 간단한 함수가 있습니다. 이 함수에 있는 if문 두개를 람다표현식을 이용하여 다음과 같은 형식으로 작성해 보세요.
lambdas = [
    lambda password : "SHORT_PASSWORD" if len(password) < 8 else None,
    lambda password : "NO_CAPITAL_LETTER_PASSWORD" if not any(c.isupper() for c in password) else None
]

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('123456789F')) # True

0개의 댓글