소개
이 섹션에서는 반복의 기본 프로세스를 살펴봅니다.
This tutorial is from open-source community. Access the source code
💡 이 튜토리얼은 영어로 번역되었습니다. 원본을 보려면 영어로 전환
이 섹션에서는 반복의 기본 프로세스를 살펴봅니다.
다양한 객체가 반복을 지원합니다.
a = 'hello'
for c in a: ## a 의 문자들을 순회합니다 (Loop over characters in a)
...
b = { 'name': 'Dave', 'password':'foo'}
for k in b: ## 딕셔너리의 키들을 순회합니다 (Loop over keys in dictionary)
...
c = [1,2,3,4]
for i in c: ## 리스트/튜플의 항목들을 순회합니다 (Loop over items in a list/tuple)
...
f = open('foo.txt')
for x in f: ## 파일의 라인들을 순회합니다 (Loop over lines in a file)
...
for
문을 생각해 봅시다.
for x in obj:
## statements
내부적으로 무슨 일이 일어날까요?
_iter = obj.__iter__() ## 이터레이터 객체 가져오기 (Get iterator object)
while True:
try:
x = _iter.__next__() ## 다음 항목 가져오기 (Get next item)
## statements ...
except StopIteration: ## 더 이상 항목 없음 (No more items)
break
for-loop
와 함께 작동하는 모든 객체는 이 하위 수준의 반복 프로토콜을 구현합니다.
예시: 리스트 수동 반복.
>>> x = [1,2,3]
>>> it = x.__iter__()
>>> it
<listiterator object at 0x590b0>
>>> it.__next__()
1
>>> it.__next__()
2
>>> it.__next__()
3
>>> it.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in ? StopIteration
>>>
반복에 대해 아는 것은 자신의 객체에 반복을 추가하려는 경우 유용합니다. 예를 들어, 사용자 정의 컨테이너를 만드는 경우입니다.
class Portfolio:
def __init__(self):
self.holdings = []
def __iter__(self):
return self.holdings.__iter__()
...
port = Portfolio()
for s in port:
...
다음 리스트를 생성합니다.
a = [1,9,4,25,16]
이 리스트를 수동으로 반복합니다. __iter__()
를 호출하여 이터레이터 (iterator) 를 얻고, __next__()
메서드를 호출하여 연속적인 요소를 얻습니다.
>>> i = a.__iter__()
>>> i
<listiterator object at 0x64c10>
>>> i.__next__()
1
>>> i.__next__()
9
>>> i.__next__()
4
>>> i.__next__()
25
>>> i.__next__()
16
>>> i.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
next()
내장 함수는 이터레이터의 __next__()
메서드를 호출하는 단축키입니다. 파일을 대상으로 사용해 보세요.
>>> f = open('portfolio.csv')
>>> f.__iter__() ## 참고: 이것은 파일 자체를 반환합니다 (Note: This returns the file itself)
<_io.TextIOWrapper name='portfolio.csv' mode='r' encoding='UTF-8'>
>>> next(f)
'name,shares,price\n'
>>> next(f)
'"AA",100,32.20\n'
>>> next(f)
'"IBM",50,91.10\n'
>>>
파일의 끝에 도달할 때까지 next(f)
를 계속 호출합니다. 어떤 일이 일어나는지 확인하세요.
경우에 따라, 특히 객체가 기존 리스트 또는 다른 반복 가능한 객체를 감싸는 경우, 자신의 객체 중 하나가 반복을 지원하도록 만들고 싶을 수 있습니다. 새로운 파일 portfolio.py
에서 다음 클래스를 정의합니다.
## portfolio.py
class Portfolio:
def __init__(self, holdings):
self._holdings = holdings
@property
def total_cost(self):
return sum([s.cost for s in self._holdings])
def tabulate_shares(self):
from collections import Counter
total_shares = Counter()
for s in self._holdings:
total_shares[s.name] += s.shares
return total_shares
이 클래스는 total_cost
속성과 같은 몇 가지 추가 메서드를 사용하여 리스트를 감싸도록 설계되었습니다. report.py
에서 read_portfolio()
함수를 수정하여 다음과 같이 Portfolio
인스턴스를 생성하도록 합니다.
## report.py
...
import fileparse
from stock import Stock
from portfolio import Portfolio
def read_portfolio(filename):
'''
Read a stock portfolio file into a list of dictionaries with keys
name, shares, and price.
'''
with open(filename) as file:
portdicts = fileparse.parse_csv(file,
select=['name','shares','price'],
types=[str,int,float])
portfolio = [ Stock(d['name'], d['shares'], d['price']) for d in portdicts ]
return Portfolio(portfolio)
...
report.py
프로그램을 실행해 보세요. Portfolio
인스턴스가 반복 가능하지 않기 때문에 심각하게 실패하는 것을 발견할 것입니다.
>>> import report
>>> report.portfolio_report('portfolio.csv', 'prices.csv')
... crashes ...
Portfolio
클래스를 수정하여 반복을 지원하도록 하여 이 문제를 해결합니다.
class Portfolio:
def __init__(self, holdings):
self._holdings = holdings
def __iter__(self):
return self._holdings.__iter__()
@property
def total_cost(self):
return sum([s.shares*s.price for s in self._holdings])
def tabulate_shares(self):
from collections import Counter
total_shares = Counter()
for s in self._holdings:
total_shares[s.name] += s.shares
return total_shares
이 변경을 수행한 후, report.py
프로그램이 다시 작동해야 합니다. 이와 함께, 새로운 Portfolio
객체를 사용하도록 pcost.py
프로그램을 수정합니다. 다음과 같이 합니다.
## pcost.py
import report
def portfolio_cost(filename):
'''
Computes the total cost (shares*price) of a portfolio file
'''
portfolio = report.read_portfolio(filename)
return portfolio.total_cost
...
작동하는지 테스트합니다.
>>> import pcost
>>> pcost.portfolio_cost('portfolio.csv')
44671.15
>>>
컨테이너 클래스를 만들 때, 단순히 반복하는 것 이상을 수행하고 싶을 때가 많습니다. Portfolio
클래스를 수정하여 다음과 같은 다른 특수 메서드를 갖도록 합니다.
class Portfolio:
def __init__(self, holdings):
self._holdings = holdings
def __iter__(self):
return self._holdings.__iter__()
def __len__(self):
return len(self._holdings)
def __getitem__(self, index):
return self._holdings[index]
def __contains__(self, name):
return any([s.name == name for s in self._holdings])
@property
def total_cost(self):
return sum([s.shares*s.price for s in self._holdings])
def tabulate_shares(self):
from collections import Counter
total_shares = Counter()
for s in self._holdings:
total_shares[s.name] += s.shares
return total_shares
이제 이 새로운 클래스를 사용하여 몇 가지 실험을 해보세요.
>>> import report
>>> portfolio = report.read_portfolio('portfolio.csv')
>>> len(portfolio)
7
>>> portfolio[0]
Stock('AA', 100, 32.2)
>>> portfolio[1]
Stock('IBM', 50, 91.1)
>>> portfolio[0:3]
[Stock('AA', 100, 32.2), Stock('IBM', 50, 91.1), Stock('CAT', 150, 83.44)]
>>> 'IBM' in portfolio
True
>>> 'AAPL' in portfolio
False
>>>
이에 대한 한 가지 중요한 관찰은, 일반적으로 코드는 Python 의 다른 부분이 일반적으로 작동하는 방식의 일반적인 어휘를 말하는 경우 "Pythonic"으로 간주된다는 것입니다. 컨테이너 객체의 경우, 반복, 인덱싱, 포함 및 기타 종류의 연산자를 지원하는 것이 이의 중요한 부분입니다.
축하합니다! 반복 프로토콜 (Iteration Protocol) 랩을 완료했습니다. LabEx 에서 더 많은 랩을 연습하여 실력을 향상시킬 수 있습니다.