Python/[코칭스터디 9기] 인공지능 AI 기초 다지기

[인공지능(AI) 기초 다지기] 1. 파이썬 기초 (4)

김초송 2023. 1. 17. 20:54

https://www.boostcourse.org/ai100/lecture/739170?isDesc=false 

 

인공지능(AI) 기초 다지기

부스트코스 무료 강의

www.boostcourse.org

 

3. 파이썬 기초 문법 2

 

- Pythonic Code

  • split & join
  • list comprehension
  • enumerate & zip
  • lambda & map & reduce
  • generator
  • asterist
colors = ['red', 'blue', 'green', 'yellow']
result = ''
for s in colors:
    result += s
result = ''.join(colors) # 좋은 코드

 

- Split & Join

  • Split
    • string type의 값을 기준값으로 나눠서 List 형태로 반환
    • split 결과값 unpacking으로 표현할 수 있음
  • Join
    • List 값 들을 하나의 문자열로 합침

 

- List Comprehension

  • 기존 List를 사용하여 간단한 다른 List를 만드는 기법
  • 포괄적인 List라는 의미
  • for + append 대신 사용, 속도도 더 빠름
result = []
for i in range(10):
    result.append(i)
result = [i for i in range(10)]

# 짝수만
result = [i for i in range(10) if i % 2 == 0]

# nested Loop
result = [i+j for i in word_1 for j in word_2]

# for i in word_1:
#     for j in word_2:

# if : filter
# i와 j가 같다면 list에 추가하지 않음
# for 앞에 if와 else에 쓰는 것도 가능
result = [i+j for i in word_1 for j in word_2 if not(i==j)]
result = [i+j if not(i==j) else i for i in case_1 for j in case_2]
  • Two dimentional list
words = 'The quick brown fox jumps over the lazy dog'.split()

stuff = [[w.upper(), w.lower(), len(w)] for w in words]

ppint.pprint(stuff) 
[['THE', 'the', 3]
 ['QUICK', 'quick', 5]
 ['BROWN', 'brown', 5]
...
  • for 문이 [ ] 안에 있을 경우 뒤의 for 문이 먼저 작동

 

- Enumerate & Zip

  • Enumerate
    • List의 element를 추출할 때 번호를 붙여서 추출 (index, value)
    • Dict에서 데이터 뽑을 때 많이 사용
for i, v in enumerate(['tic', 'tac', 'toe']):
    print (i, v)

# 0 tic
# 1 tac
# 2 toe

mylist = ['a', 'b', 'c', 'd']
list(enumerate(mylist)) # list의 index와 value를 unpacking 하여 list로 저장
# [(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd')]

my_string = 'Artificial intelligence (AI), is intelligence demonstrated by machines,
    unlike the natural intelligence displayed by humans and animals.'
# dict 타입으로 선언해서 사용
{v.lower() : i for i, v in enumerate(my_string.split())}

# 중복되는 글자 제거
list(set(text.split()))
  • Zip
    • 두 개의 list 값을 병렬적으로 묶어서 추출함
alist = ['a1', 'a2', 'a3']
blist = ['b1', 'b2', 'b3']
for a, b in zip(alist, blist): # 병렬적으로 값을 추출
    print (a,b)
# a1 b1
# a2 b2
# a3 b3

[ [a, b] for a, b in zip(alist, blist) ]
# [['a1', 'b1'], ['a2', b2'], ['a3', 'b3']]

[ c for c in zip(alist, blist) ] # 튜플 타입
list(zip(alist, blist))
# [('a1', 'b1'), ('a2', b2'), ('a3', 'b3')]

list(enumerate(zip(alist, blist)))
# [(0, ('a1', 'b1')), (1, ('a2', b2')), (2, ('a3', 'b3'))]

 

- Lambda & Map & Reduce

  • Lambda
    • 함수 이름 없이 쓸 수 있는 익명 함수
    • 함수의 이름을 따로 지정해주지 않음
    • python 3부터 권장하지 않으나 여전히 많이 쓰임
    • 테스트 할 때 어려움이 있음
    • docstring 지원이 미비 -> 문서화 어려움
    • 휘발성 함수일 때 많이 사용
def f(x, y): return x + y
f(10, 50)

f = lambda x, y: x + y
f(10, 50)

(lambda x, y : x + y)(10, 50)

# 셋 다 같은 결과 60
  • Map
    • 두 개 이상의 List에도 적용 가능
    • 시퀀스형 데이터가 있을 때 각각 함수를 적용하고 결과값을 받음
    • if filter 사용 가능
    • list(map(function, 함수의 parameter)) -> list를 붙여주지 않으면 메소드 주소값만 리턴
    • 실행 시점의 값을 생성, 메모리 효율적
    • 권장하지 않음 -> 사용자가 이해하기 어려움
      [ f(value) for value in parmeter ] 로 바꿔쓰기
ex = list(range(1, 6))

# 둘다 같은 코드
list(map(lambda x : x ** 2 if x % 2 == 0 else x, ex))
[ value ** 2 if value % 2 == 0 else value for values in ex ]

# [1, 4, 3, 16, 5]
  • Reduce
    • map과 달리 List에 똑같은 함수를 적용해서 차례대로 통합
    • 대용량 데이터를 다룰 때 많이 사용
    • 코드의 직관성이 떨어져서 python 3에서 권장하지 않음
    • 머신러닝 코드에서는 여전히 많이 사용 중
from functools import reduce
print(reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]))

결과값

 

- Iterable Object

  • 시퀀스형 자료형에서 데이터를 순서대로 추출하는 Object
  • 내부적으로 __iter__와 __next__가 구현되어 있음
  • iter()와 next() 로 iterable 객체를 iterator object로 사용
  • List 객체는 각각의 값의 주소값을 갖고 있음
  • iterable 객체는 다음의 위치만 알고 있음
cities = ["Seoul", "Busan", "Jeju"]

# initialize the object
iter_obj = iter(cities) # iter_obj는 memory address
print(next(iter_obj)) # 실제 값을 보려면 next() 사용
print(next(iter_obj)) # 다음 위치를 계속 반환
print(next(iter_obj))
next(iter_obj) # 끝나면 stop

 

- Generator

  • Iterable Object는 Generator 개념을 포함...?
  • Iterable Object를 특수한 형태로 사용해주는 함수
  • element가 사용되는 시점에 값을 메모리에 반환
    yield를 사용해 한번에 하나의 element만 반환 (그 전에는 주소값만 가지고 있음)
  • iterable object가 next를 부르는 것과 똑같음
  • 대용량일 때 특히 권장
  • iterator는 generator보다 훨씬 큰 메모리를 사용
def general_list(value):
    result = []
    for i in range(value):
        result.append(i)
    return result
# 0부터 49까지 리스트
# 520 byte

def geneartor_list(value):
    result = []
    for i in range(value): 
        yield i

generator_list() # 제너레이터 형태
    
# 값을 보려면 for문과 함께 사용해서 값을 호출해야 함
# 호출할 때 데이터를 출력
for a in gerater_list():
    print(a)
# 112 byte

list(generator_list()) # list를 쓰면 메모리에 값들을 올림
  • Generator Comprehension
    • generator 형태의 list 생성
    • = generator expression
    • [] 대신 ()를 사용하여 표현
gen_ex = (n*n for n in range(500))
print(type(gen_ex))
# <class 'generator'>

# 사용하려면 for문이나 list와 함께 사용
list(gen_ex)
  • List 타입의 데이터를 반화해주는 함수는 Generator 권장
    읽기 쉬움
    중간 과정에서 loop가 중단될 수 있을 때
  • 큰 데이터를 핸들링할 때 Generator 권장
  • 파일 데이터 처리

 

- Passing Arguments

  1. Keyword argument
  2. Default argument
  3. Variable-length argument (가변 인자)
  • Keyword Arguments
    • 함수에 입력되는 parameter의 변수명(keyword)를 사용
    • function(parameter1 = argument1, parmeter2 = argument2)
    • 키워드가 없으면 차례대로
  • Default Arguments
    • parameter의 기본값을 사용해서 입력하지 않으면 default 값 사용
    • def function(parameter1, parameter2=defaut_value)
    • default value가 없는데 인자를 넣지 않으면 에러 발생
  • Variable-length Arguments 
    • 함수의 parameter 개수가 정해지지 않았을 때 (다항 방정식, 마트 물건 계산 함수)
    • Asterist(*) 기호를 사용
    • *args
    • keyword arguments와 함께 argument 추가 가능
    • 기존의 parameter 이후에 나오는 값을 tuple로 저장함
    • 가변인자는오직 한 개만 맨 마지막 parameter 위치에 사용가능
    • unpacking도 가능
def asterisk_test(a, b, *args):
    print(list(args))
    print(type(args))
    return a+b+sum(args)
    
print(asterisk_test(1, 2, 3, 4, 5))
# [3, 4, 5]
# <class 'tuple'>
# 15
  • Keyword Variable-length (키워드 가변 인자)
    • parameter 이름을 따로 지정하지 않고 입력하는 방법
    • asterisk(*) 두 개를 사용하여 함수의 parameter를 표시
    • **kwargs
    • 입력된 값은 dict type
    • 가변인자는 오직 한 개만 기존 가변인자 다음에 사용
    • 키워드로 인자가 들어가면 뒤에 가변인자 사용 불가능
def kwargs_test_1(**kwargs):
    print(kwargs)
    print(type(kwargs))
    
kwargs_test_1(first=3, second=4, third=5)
# {'first': 3, 'second': 4, 'third': 5}
# <class 'dict'>
def kwargs_test_3(one, two=2, *args, **kwargs):
    print(one+two+sum(args))
    print(args)
    print(kwargs)

kwargs_test_3(3, 4, 5, 6, 7, 8, 9, first=3, second=4, third=5)
# one = 3 / two = 4
# args = (5, 6, 7, 8, 9)
# kwargs = {'first': 3, 'second': 4, 'third': 5}

# 뒤에 가변인자 넣으면 에러 발생
kwargs_test_3(3, 4, 5, 6, 7, 8, 9, first=3, second=4, third=5 , 10)

# 앞에 키워드 인자를 넣어도 에러 발생 (뒤에 가변인자 못 넣음)
kwargs_test_3(one=3, two=4, 5, 6, 7, 8, 9, first=3, second=4, third=5 , 10)

 

- Asterisk = unpacking a container

  • tuple, dict 등 자료형에 들어가 있는 값을 unpacking
  • argument로 들어갈 때 값들을 풀어서 넣음
  • 함수의 입력값, zip 등에 유용하게 사용
  • dict 풀어줄 땐 **로 ( a= 1, b=2, c=3)
def asterisk_test(a, *args):
    print(a, *args)
    print(a, args) # 변수의 값 하나로만 들어감
    print(type(args))

asterisk_test(1, *(2, 3, 4, 5, 6))
# *가 없으면 () 가 하나의 값으로 들어감
# args 는 튜플 형태기 때문에 다시 튜플로 묶여 들어감

# 1 2 3 4 5 6 
# 1 (2, 3, 4, 5, 6)
# <class 'tuple'>

asterisk_test(1, (2, 3, 4, 5, 6))
# 1 (2, 3, 4, 5, 6)
# 1 ((2, 3, 4, 5, 6),)
# <class 'tuple'>