goodthings4me.tistory.com
파이썬 기본기 UP 2 - 취미 삼아 배우는 파이썬의 코딩 스킬 업을 위해 파이썬 중급(또는 고급) 내용을 나름대로 찾아서 정리하는 중이며, 이번 포스팅은 참조변수, 리스트 컴프리헨션, 이터레이터, 컨텍스트관리자, 제너레이터 등의 내용을 강의하는 자료가 있어서 스터디 후 나에게 필요한 부분만 정리해보았다. (나중에 필요 시 참고하기 위한 자료임)
정리한 주요 내용은 다음과 같다.
- 자료형과 참조변수
- 리스트 컴프리헨션
- 반복 가능 객체(iterator)
- with 문과 컨텍스트 매니저(context manager)
- 제너레이터(generator)와 yield
■ 자료형과 참조변수
- 파이썬의 모든 것은 객체이다.
- 파이썬은 객체지향 프로그래밍 언어이다.
- C 언어는 변수가 생성되고 변수에 값이 저장되는 구조(변수 중심)인 반면, 파이썬은 객체가 중심이 되며, 객체를 참조하는 참조 변수를 통해 객체에 접근할 수 있다.
- 파이썬의 변수에는 동적으로 참조하는 객체가 저장된다.
- a=100 시, 100이라는 정수형 객체가 먼저 생성되고, 객체에 대한 참조변수 a가 100 객체를 참조한다.
- a=200 하면, 200이라는 정수형 객체가 생성되고, 참조 변수가 객체 200을 참조한다.(객체 재할당)
- 파이썬의 모든 객체는 고유한 id를 가진다.(객체 구분)
- '=' 연산자는 객체에 대한 참조를 만들거나 변경시킨다.
- 객체를 여러 변수가 동시에 참조할 수 있다.
a = 100
print(id(100)) # 1859736327632 --> 100을 가지는 정수형 객체의 id
print(id(a)) # 1859736327632 --> a가 참조하는 객체의 id를 반환
b = a
print(id(b)) # 2000605828560
a = 300
print(id(a)) # 2217609689840 --> a는 새로운 객체를 참조
■ 리스트 컴프리헨션 (List Comprehension)
# 반복 가능 객체로 리스트 생성할 수 있는 기능
a = [1, 2, 3, 4, 5, 6, 7, 8]
lst = []
for i in a:
lst.append(i**2)
print(lst) # [1, 4, 9, 16, 25, 36, 49, 64]
ml = list(map(lambda x: x**2, a))
print(ml) # [1, 4, 9, 16, 25, 36, 49, 64]
ch = [i**2 for i in a]
print(ch) # [1, 4, 9, 16, 25, 36, 49, 64]
# list(), filter() lambda 함수 이용한 필터링
ages = [24, 39, 18, 40, 13, 54]
adult_ages = list(filter(lambda x: x >= 19, ages))
print(adult_ages) # [24, 39, 40, 54]
ad_ages = [x for x in ages if x >= 19]
print(ad_ages) # [24, 39, 40, 54]
# 이중 for 문을 리스트 컴프리헨션으로 구현
xy = []
for x in [1, 2, 3]:
for y in [2, 4, 6]:
xy.append(x * y)
print(xy) # [2, 4, 6, 4, 8, 12, 6, 12, 18]
c_xy = [x * y for x in [1, 2, 3] for y in [2, 4, 6]]
print(c_xy) # [2, 4, 6, 4, 8, 12, 6, 12, 18]
# 2와 3의 배수 구하기
cmulti = []
for n in range(1, 31):
if n % 2 == 0 and n % 3 == 0:
cmulti.append(n)
print(cmulti) # [6, 12, 18, 24, 30]
print([n for n in range(1, 31) if n % 2 == 0 and n % 3 == 0]) # [6, 12, 18, 24, 30]
print([n for n in range(1, 31) if n % 2 == 0 if n % 3 == 0]) # [6, 12, 18, 24, 30]
■ 반복 가능 객체(iterable 객체)와 반복자 객체(iterator 객체)
반복 가능 자료형인 리스트, 튜플, 문자열, 딕셔너리, 집합, 파일, range 등에 내장함수 iter()를 이용해 반복자 객체(iterator)로 만들고, next(객체명) 또는 객체명.__next__()로 하나씩 꺼낸다.
즉, 반복 가능 객체(iterable 객체 - list, tuple, range 등)에 대해 iter() 함수를 적용[iter(iterable 객체)]하여 iterator(데이터를 순차적으로 꺼내 이용할 수 있는 객체)를 생성하고 next() 함수(또는 __next()__)로 값을 추출(next() 반복)한다. 이때, 값이 없으면 StopIteration 예외 발생함
l = [10, 20, 30]
#print(l.next()) # AttributeError: 'list' object has no attribute 'next'
l_iter = iter(l)
print(type(l_iter), l_iter)
# <class 'list_iterator'> <list_iterator object at 0x0000019DBF8EDCA0>
print(next(l_iter)) # 10
print(next(l_iter)) # 20
print(l_iter.__next__()) # 30
#print(l_iter.__next__()) # StopIteration
# 리스트 객체에는 __iter__ 메서드가 있다. 이 __iter__ 메서드를 호출하면 iterator가 나온다
print('__iter__' in dir(l)) # True
print(l.__iter__)
# <method-wrapper '__iter__' of list object at 0x0000018745879F40>
print(l.__iter__())
# <list_iterator object at 0x0000026A40B43F70>
l_it = l.__iter__()
print(next(l_it)) # 10
print(next(l_it)) # 20
print(next(l_it)) # 30
# 정수형 객체는 반복자 객체로 변환 불가
n = 100
#n_iter = iter(n) # TypeError: 'int' object is not iterable
# range() 함수는 range 형 객체를 만든다. range 형 객체를 iter() 함수로 iterator로 변환해보자
print(type(range(3)), range(3)) # <class 'range'> range(0, 3)
r_iter = iter(range(3))
print(type(r_iter), r_iter)
# <class 'range_iterator'> <range_iterator object at 0x000002CB6FB4E550>
print(next(r_iter)) # 0
print(next(r_iter)) # 1
print(next(r_iter)) # 2
#print(next(r_iter)) # StopIteration
# 주어진 인자값을 기준으로 시작하고 증가하는 반복자 개체 만들기
class OddCounter:
''' 주어진 n부터 m씩 증가하는 값을 반환하는 클래스 '''
def __init__(self, n, m):
self.n = n
self.m = m
def __iter__(self):
return self
def __next__(self):
t = self.n
self.n += self.m
return t
my_count = OddCounter(5, 7)
print(next(my_count)) # 5
print(my_count.__next__()) # 12
print(my_count.__next__()) # 19
print(my_count.__next__()) # 26
# my coding practice)
class MakeListIterator:
def __init__(self, seq_obj):
self.idx = 0
self.seq_obj = seq_obj
self.new_iter = ''
def __iter__(self):
self.new_iter = 'y'
return self
def __next__(self):
if not self.new_iter:
print('iterator 객체 생성을 먼저 해주세요!')
return
if self.idx >= len(self.seq_obj):
raise StopIteration
lst_n = self.seq_obj[self.idx]
self.idx += 1
return lst_n
lst = MakeListIterator([20, 18, 30])
print(lst.__iter__()) # <__main__.MakeListIterator object at 0x000001CD3A6489A0>
print(lst.new_iter) # y
print(lst.__next__()) # 20
print(lst.__next__()) # 18
print(lst.__next__()) # 30
#print(lst.__next__()) # StopIteration
▷ 반복 가능한 객체를 위한 내장함수 - min(), max(), all(), any(), ascii(), bool(), filter(), iter() 등
# all() any() 함수
# - all() 반복 가능한 항목들이 모두 참일 때만 참을 반환함
# - any() 반복 가능한 항목들 중에서 참이 하나라도 있을 때 참을 반환함
l1 = [1, 2, 3, 4]
l2 = [2, 5, 0, 9]
l3 = [1, '0', 2, 3]
l4 = [1, [], 2, 3]
l5 = [[], 0, {}, '']
print(all(l1), all(l2), all(l3), all(l4)) # True False True False
print(any(l1), any(l2), any(l3), any(l4), any(l5)) # True True True True False
☞ 함수형 프로그래밍 모듈
# itertools - 반복자(iterator) 생성 모듈
# functools - 다른 함수를 반환할 수 있는 모듈, reduce() 등
# operator - 표준 연산자를 함수 형태로 다룰 수 있게 하는 모듈
■ with 문과 컨텍스트 매니저(관리자)
'컨텍스트 매니저'란, with문에서 사용하도록 설계된 객체를 말하는데, with 구문 body의 앞부분과 뒷부분 실행 코드를 대신할 수 있다. with 구문 사용 시, open()이 간단해지고, finally 절도 필요없어짐
with expression as thing:
body
컨텍스트 매니저(context manager),
- with 구문의 expression 호출(실행) --> context manager가 자동적으로 __enter__() 실행 후 반환값(결과값)을 as의 변수 thing에 지정하고,
- 그 후 thing를 사용하는 body문 실행 후
- with문을 벗어날 시, context manager는 __exit__()를 자동 실행시킴
(예외적인 상황이 생겨도 __exit__() 메소드는 호출이 보장됨)
즉, __enter__()는 body문에 들어가기 전에 호출되고 with 문 탈출 시 __exit__() 메소드 호출함
- 파이썬의 file 객체에는 __enter__(), __exit__() 메소드가 구현되어있다.
- 이 객체는 file object 자신을 반환한다. __exit__() 메소드는 당연히 파일을 close 한다.
try:
f = open('foo.txt', 'r')
except FileNotFoundError as e:
print(str(e))
else: # try error 없을 때
data = f.read()
f.close()
try:
f = open('file.txt', 'w')
try:
f.write('finally 절은 try문 수행도중 발생하는 예외에 관계없이 항상 수행된다.')
finally:
f.close()
except IOError:
print('oops!')
위 코드를 아래처럼 사용
try:
with open('file.txt', 'w') as f:
f.write('위 try 문을 이렇게 쓸 수 있다.')
except IOError:
print('oops!')
■ 제너레이터(generator)와 yield
제너레이터 객체는 모든 값을 메모리에 올려두고 이용하는 것이 아니라 필요 시 생성하고 반환하는 일을 한다.
my_gen = (x for x in range(1, 4)) # [] 대신 () 사용 시 제너레이터 표현식임
print(type(my_gen), my_gen)
# <class 'generator'> <generator object <genexpr> at 0x000001FC4888BD60>
for n in my_gen:
print(n, end=' ') # 1 2 3
- 위 코드를 보면 반복자(iterator)와 동일한 일을 하는 것처럼 보이지만,
- 여기서 생성된 '1 2 3 '을 미리 메모리에 만들어 두는 것이 아니라 for 문에서 필요로 할 때 마다 my_gen으로부터
- 받아오는 것이며, 메모리에 보관하지 않는다는 점이 반복자와 다르다.(이를 lazy evaluation이라 함)
yield 문은 return 문과 유사하지만, 제너레이터를 반환한다는 점에서 차이가 있다.
def create_xgen():
lst = range(1, 4)
for x in lst:
print(f'x: {x}')
return x
r = create_xgen()
print(r) # x: 1 1
def create_gen(): # yield 문을 가진 제너레이터
lst = range(1, 4)
for y in lst:
print(f'y: {y}')
yield y
y = create_gen()
print(y)
# <generator object create_gen at 0x0000021A8CC81EB0>
for n in y: # for 문에 의해 호출될 때마다 객체 반환
print(n)
# y: 1
# 1
# y: 2
# 2
# y: 3
# 3
# 다시 실행 시, 제너레이터는 한 번 실행하면 아무것도 반환하지 않음
for n in y:
print(n, end=' ') # 아무것도 출력 안됨
## 일반 함수와 제너레이터 사용한 루프의 처리시간 비교
# - 일반함수는 피보나치 수열을 생성하여 리스트에 넣고 for문을 통해 출력
# - 제너레이터는 피보나치 수열을 계산할 때마다 for문에 넘겨준다
import time
def fibon(n):
a = b = 1
result = []
for i in range(n):
result.append(a)
a, b = b, a + b
return result
start_t = time.time()
for x in fibon(100000):
pass
end_t = time.time()
print(f'수행 시간 : {end_t - start_t}')
# 수행 시간 : 0.3949697017669678
def fibon_y(n):
start_t = time.time()
a = b = 1
for i in range(n):
yield a
a, b = b, a + b
start_yt = time.time()
for x in fibon_y(100000):
pass
end_yt = time.time()
print(f'수행 시간 : {end_yt - start_yt}')
# 수행 시간 : 0.1046915054321289
■ docstring
## - docstring
def square(a):
'''입력값 a의 제곱을 반환하는 함수 '''
return a**2
print(square.__doc__) # 입력값 a의 제곱을 반환하는 함수
print(help(square))
# Help on function square in module __main__:
# square(a)
# 입력값 a의 제곱을 반환하는 함수
# None
[출처] 널날한 교수의 고급 파이썬
'코딩 연습 > 코딩배우기' 카테고리의 다른 글
장고(Django) 웹 프레임워크 프로젝트 예제로 알아보는 파이썬 웹 프로그래밍 연습 (0) | 2021.07.29 |
---|---|
파이썬 장고(Django) 웹 프레임워크로 웹사이트 만들기 예제 (프로젝트 생성 후 배포까지 해보기) (0) | 2021.07.28 |
[Python] 파이썬 기본기 UP - 함수, 클래스, DB 다루기 (0) | 2021.07.20 |
12 간지 띠 알아보기 (파이썬 기초 예제) (0) | 2021.07.18 |
[Python] 파이썬 기초(표준 라이브러리) 요약 - 참고용 (0) | 2021.07.14 |
댓글