리스트, 딕셔너리, 세트 소개

Intermediate

This tutorial is from open-source community. Access the source code

소개

이 섹션에서는 리스트 (lists), 딕셔너리 (dictionaries), 그리고 세트 (sets) 에 대해 논의합니다.

이것은 가이드 실험입니다. 학습과 실습을 돕기 위한 단계별 지침을 제공합니다.각 단계를 완료하고 실무 경험을 쌓기 위해 지침을 주의 깊게 따르세요. 과거 데이터에 따르면, 이것은 중급 레벨의 실험이며 완료율은 74%입니다.학습자들로부터 88%의 긍정적인 리뷰율을 받았습니다.

개요

프로그램은 종종 많은 객체와 함께 작동해야 합니다.

  • 주식 포트폴리오
  • 주가 테이블

사용할 수 있는 세 가지 주요 선택 사항이 있습니다.

  • 리스트 (Lists). 정렬된 데이터.
  • 딕셔너리 (Dictionaries). 정렬되지 않은 데이터.
  • 세트 (Sets). 고유 항목의 정렬되지 않은 컬렉션.

컨테이너로서의 리스트 (Lists)

데이터의 순서가 중요한 경우 리스트를 사용합니다. 리스트는 어떤 종류의 객체든 담을 수 있다는 것을 기억하세요. 예를 들어, 튜플 (tuple) 의 리스트가 있습니다.

portfolio = [
    ('GOOG', 100, 490.1),
    ('IBM', 50, 91.3),
    ('CAT', 150, 83.44)
]

portfolio[0]            ## ('GOOG', 100, 490.1)
portfolio[2]            ## ('CAT', 150, 83.44)

리스트 구성

처음부터 리스트를 구성합니다.

records = []  ## Initial empty list

## .append() 를 사용하여 더 많은 항목을 추가합니다.
records.append(('GOOG', 100, 490.10))
records.append(('IBM', 50, 91.3))
...

파일에서 레코드를 읽을 때의 예시입니다.

records = []  ## Initial empty list

with open('portfolio.csv', 'rt') as f:
    next(f) ## Skip header
    for line in f:
        row = line.split(',')
        records.append((row[0], int(row[1]), float(row[2])))

컨테이너로서의 딕셔너리 (Dicts)

딕셔너리는 (키 이름으로) 빠른 무작위 조회 (random lookup) 를 원할 때 유용합니다. 예를 들어, 주식 가격의 딕셔너리가 있습니다.

prices = {
   'GOOG': 513.25,
   'CAT': 87.22,
   'IBM': 93.37,
   'MSFT': 44.12
}

다음은 몇 가지 간단한 조회 예시입니다.

>>> prices['IBM']
93.37
>>> prices['GOOG']
513.25
>>>

딕셔너리 구성

처음부터 딕셔너리를 구성하는 예시입니다.

prices = {} ## Initial empty dict

## 새로운 항목 삽입
prices['GOOG'] = 513.25
prices['CAT'] = 87.22
prices['IBM'] = 93.37

파일의 내용으로 딕셔너리를 채우는 예시입니다.

prices = {} ## Initial empty dict

with open('prices.csv', 'rt') as f:
    for line in f:
        row = line.split(',')
        prices[row[0]] = float(row[1])

참고: prices.csv 파일에서 이 코드를 실행하면 거의 작동하지만, 마지막에 빈 줄이 있어 충돌이 발생합니다. 이를 해결하기 위해 코드를 수정하는 방법을 찾아야 합니다 (연습문제 2.6 참조).

딕셔너리 조회

키의 존재 여부를 테스트할 수 있습니다.

if key in d:
    ## YES
else:
    ## NO

존재하지 않을 수 있는 값을 조회하고, 존재하지 않는 경우 기본값을 제공할 수 있습니다.

name = d.get(key, default)

예시:

>>> prices.get('IBM', 0.0)
93.37
>>> prices.get('SCOX', 0.0)
0.0
>>>

복합 키 (Composite keys)

Python 에서는 거의 모든 유형의 값을 딕셔너리 키로 사용할 수 있습니다. 딕셔너리 키는 불변 (immutable) 타입이어야 합니다. 예를 들어, 튜플 (tuple) 과 같습니다.

holidays = {
  (1, 1) : 'New Years',
  (3, 14) : 'Pi day',
  (9, 13) : "Programmer's day",
}

접근 방법:

>>> holidays[3, 14]
'Pi day'
>>>

리스트 (list), 세트 (set), 또는 다른 딕셔너리는 딕셔너리 키로 사용할 수 없습니다. 리스트와 딕셔너리는 가변 (mutable) 이기 때문입니다.

세트 (Sets)

세트는 정렬되지 않은 고유 항목들의 모음입니다.

tech_stocks = { 'IBM','AAPL','MSFT' }
## Alternative syntax
tech_stocks = set(['IBM', 'AAPL', 'MSFT'])

세트는 멤버십 테스트 (membership tests) 에 유용합니다.

>>> tech_stocks
set(['AAPL', 'IBM', 'MSFT'])
>>> 'IBM' in tech_stocks
True
>>> 'FB' in tech_stocks
False
>>>

세트는 중복 제거에도 유용합니다.

names = ['IBM', 'AAPL', 'GOOG', 'IBM', 'GOOG', 'YHOO']

unique = set(names)
## unique = set(['IBM', 'AAPL','GOOG','YHOO'])

추가적인 세트 연산:

unique.add('CAT')        ## Add an item
unique.remove('YHOO')    ## Remove an item

s1 = { 'a', 'b', 'c'}
s2 = { 'c', 'd' }
s1 | s2                 ## Set union { 'a', 'b', 'c', 'd' }
s1 & s2                 ## Set intersection { 'c' }
s1 - s2                 ## Set difference { 'a', 'b' }

이 연습에서는 이 과정의 나머지 부분에서 사용될 주요 프로그램 중 하나를 구축하기 시작합니다. report.py 파일에서 작업을 수행하십시오.

연습 문제 2.4: 튜플의 리스트

portfolio.csv 파일에는 포트폴리오에 있는 주식 목록이 포함되어 있습니다. 연습 문제 1.30 에서 이 파일을 읽고 간단한 계산을 수행하는 함수 portfolio_cost(filename)을 작성했습니다.

작성한 코드는 다음과 유사해야 합니다.

## pcost.py

import csv

def portfolio_cost(filename):
    '''Computes the total cost (shares*price) of a portfolio file'''
    total_cost = 0.0

    with open(filename, 'rt') as f:
        rows = csv.reader(f)
        headers = next(rows)
        for row in rows:
            nshares = int(row[1])
            price = float(row[2])
            total_cost += nshares * price
    return total_cost

이 코드를 대략적인 가이드로 사용하여 새 파일 report.py를 만듭니다. 해당 파일에서 주어진 포트폴리오 파일을 열어 튜플의 리스트로 읽어 들이는 함수 read_portfolio(filename)을 정의합니다. 이렇게 하려면 위의 코드에 몇 가지 사소한 수정을 해야 합니다.

먼저, total_cost = 0을 정의하는 대신, 처음에는 빈 리스트로 설정된 변수를 만듭니다. 예를 들어:

portfolio = []

다음으로, 비용을 합산하는 대신, 각 행을 바로 이전 연습에서 했던 것처럼 튜플로 변환하고 이 리스트에 추가합니다. 예를 들어:

for row in rows:
    holding = (row[0], int(row[1]), float(row[2]))
    portfolio.append(holding)

마지막으로, 결과 portfolio 리스트를 반환합니다.

함수를 대화형으로 실험해 보십시오 (이 작업을 수행하려면 먼저 인터프리터에서 report.py 프로그램을 실행해야 함을 다시 한 번 상기시켜 드립니다).

힌트: 터미널에서 파일을 실행할 때 -i를 사용하십시오.

>>> portfolio = read_portfolio('/home/labex/project/portfolio.csv')
>>> portfolio
[('AA', 100, 32.2), ('IBM', 50, 91.1), ('CAT', 150, 83.44), ('MSFT', 200, 51.23),
    ('GE', 95, 40.37), ('MSFT', 50, 65.1), ('IBM', 100, 70.44)]
>>>
>>> portfolio[0]
('AA', 100, 32.2)
>>> portfolio[1]
('IBM', 50, 91.1)
>>> portfolio[1][1]
50
>>> total = 0.0
>>> for s in portfolio:
        total += s[1] * s[2]

>>> print(total)
44671.15
>>>

이렇게 생성한 튜플의 리스트는 2 차원 배열과 매우 유사합니다. 예를 들어, portfolio[row][column]과 같은 조회를 사용하여 특정 열과 행에 접근할 수 있습니다. 여기서 rowcolumn은 정수입니다.

즉, 다음과 같은 문을 사용하여 마지막 for 루프를 다시 작성할 수도 있습니다.

>>> total = 0.0
>>> for name, shares, price in portfolio:
            total += shares*price

>>> print(total)
44671.15
>>>

연습 문제 2.5: 딕셔너리의 리스트

연습 문제 2.4 에서 작성한 함수를 가져와 튜플 대신 딕셔너리로 포트폴리오의 각 주식을 나타내도록 수정합니다. 이 딕셔너리에서 "name", "shares", "price" 필드 이름을 사용하여 입력 파일의 서로 다른 열을 나타냅니다.

연습 문제 2.4 에서와 동일한 방식으로 이 새로운 함수를 실험해 보십시오.

>>> portfolio = read_portfolio('/home/labex/project/portfolio.csv')
>>> portfolio
[{'name': 'AA', 'shares': 100, 'price': 32.2}, {'name': 'IBM', 'shares': 50, 'price': 91.1},
    {'name': 'CAT', 'shares': 150, 'price': 83.44}, {'name': 'MSFT', 'shares': 200, 'price': 51.23},
    {'name': 'GE', 'shares': 95, 'price': 40.37}, {'name': 'MSFT', 'shares': 50, 'price': 65.1},
    {'name': 'IBM', 'shares': 100, 'price': 70.44}]
>>> portfolio[0]
{'name': 'AA', 'shares': 100, 'price': 32.2}
>>> portfolio[1]
{'name': 'IBM', 'shares': 50, 'price': 91.1}
>>> portfolio[1]['shares']
50
>>> total = 0.0
>>> for s in portfolio:
        total += s['shares']*s['price']

>>> print(total)
44671.15
>>>

여기에서 각 항목의 서로 다른 필드는 숫자 열 번호 대신 키 이름으로 액세스됨을 알 수 있습니다. 결과 코드가 나중에 읽기 더 쉽기 때문에 이 방법이 선호되는 경우가 많습니다.

큰 딕셔너리와 리스트를 보는 것은 지저분할 수 있습니다. 디버깅을 위해 출력을 정리하려면 pprint 함수를 사용하는 것을 고려하십시오.

>>> from pprint import pprint
>>> pprint(portfolio)
[{'name': 'AA', 'price': 32.2, 'shares': 100},
    {'name': 'IBM', 'price': 91.1, 'shares': 50},
    {'name': 'CAT', 'price': 83.44, 'shares': 150},
    {'name': 'MSFT', 'price': 51.23, 'shares': 200},
    {'name': 'GE', 'price': 40.37, 'shares': 95},
    {'name': 'MSFT', 'price': 65.1, 'shares': 50},
    {'name': 'IBM', 'price': 70.44, 'shares': 100}]
>>>

연습 문제 2.6: 컨테이너로서의 딕셔너리

딕셔너리는 정수가 아닌 다른 인덱스를 사용하여 항목을 조회하려는 경우 항목을 추적하는 데 유용한 방법입니다. Python 셸에서 딕셔너리를 가지고 놀아보십시오.

>>> prices = { }
>>> prices['IBM'] = 92.45
>>> prices['MSFT'] = 45.12
>>> prices
... 결과를 확인하십시오 ...
>>> prices['IBM']
92.45
>>> prices['AAPL']
... 결과를 확인하십시오 ...
>>> 'AAPL' in prices
False
>>>

prices.csv 파일에는 일련의 주가가 포함되어 있습니다. 파일은 다음과 같습니다.

"AA",9.22
"AXP",24.85
"BA",44.85
"BAC",11.27
"C",3.72
...

이와 같은 일련의 가격을 딕셔너리로 읽어 들이는 함수 read_prices(filename)을 작성하십시오. 여기서 딕셔너리의 키는 주식 이름이고 딕셔너리의 값은 주가입니다.

이렇게 하려면 빈 딕셔너리에서 시작하여 위에서 했던 것처럼 값을 삽입하십시오. 그러나 이제 파일에서 값을 읽고 있습니다.

이 데이터 구조를 사용하여 주어진 주식 이름의 가격을 빠르게 조회할 것입니다.

이 부분에 필요한 몇 가지 작은 팁이 있습니다. 먼저, 이전과 마찬가지로 csv 모듈을 사용해야 합니다. 여기서는 바퀴를 다시 발명할 필요가 없습니다.

>>> import csv
>>> f = open('/home/labex/project/prices.csv', 'r')
>>> rows = csv.reader(f)
>>> for row in rows:
        print(row)


['AA', '9.22']
['AXP', '24.85']
...
[]
>>>

또 다른 작은 복잡한 점은 prices.csv 파일에 빈 줄이 있을 수 있다는 것입니다. 위의 마지막 데이터 행이 빈 목록인 것을 확인하십시오. 즉, 해당 줄에 데이터가 없었습니다.

이로 인해 프로그램이 예외와 함께 종료될 수 있습니다. tryexcept 문을 사용하여 적절하게 이를 잡으십시오. 생각: if 문을 사용하여 잘못된 데이터를 방지하는 것이 더 나을까요?

read_prices() 함수를 작성했으면 대화형으로 테스트하여 작동하는지 확인하십시오.

>>> prices = read_prices('/home/labex/project/prices.csv')
>>> prices['IBM']
106.28
>>> prices['MSFT']
20.89
>>>

연습 문제 2.7: 은퇴 가능 여부 확인

이 모든 작업을 report.py 프로그램에 이익/손실을 계산하는 몇 가지 추가 문을 추가하여 함께 묶습니다. 이러한 문은 연습 문제 2.5 의 주식 목록과 연습 문제 2.6 의 가격 딕셔너리를 가져와 포트폴리오의 현재 가치와 이익/손실을 계산해야 합니다.

요약

축하합니다! 컨테이너 랩을 완료했습니다. LabEx 에서 더 많은 랩을 연습하여 기술을 향상시킬 수 있습니다.