2  3장: 내장 데이터 구조, 함수, 파일

파이썬의 핵심 자료구조인 리스트, 튜플, 사전, 세트를 깊이 있게 다룹니다.

3 3장: 내장 데이터 구조, 함수, 파일

튜플, 리스트, 딕셔너리, 세트 등 파이썬의 핵심 데이터 구조와 함수 정의 및 파일 처리를 다룹니다.

3.1 튜플 (Tuple)

변경 불가능한(immutable) 시퀀스 자료형입니다.

import warnings
warnings.filterwarnings('ignore')


import matplotlib.pyplot as plt
# Matplotlib 한글 폰트 설정 (macOS용)
plt.rc('font', family='AppleGothic')
plt.rc('axes', unicode_minus=False)

3.2 튜플 (Tuple)

변경 불가능한 시퀀스 자료형인 튜플의 활용법을 익힙니다.

tup = (4, 5, 6)
tup
(4, 5, 6)
tup = 4, 5, 6
tup
(4, 5, 6)
tuple([4, 0, 2])
tup = tuple('string')
tup
('s', 't', 'r', 'i', 'n', 'g')
tup[0]
's'
nested_tup = (4, 5, 6), (7, 8)
nested_tup
nested_tup[0]
nested_tup[1]
(7, 8)
tup = tuple(['foo', [1, 2], True])
tup[2] = False
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[6], line 2
      1 tup = tuple(['foo', [1, 2], True])
----> 2 tup[2] = False

TypeError: 'tuple' object does not support item assignment
tup[1].append(3)
tup
('foo', [1, 2, 3], True)
(4, None, 'foo') + (6, 0) + ('bar',)
(4, None, 'foo', 6, 0, 'bar')
('foo', 'bar') * 4
('foo', 'bar', 'foo', 'bar', 'foo', 'bar', 'foo', 'bar')
tup = (4, 5, 6)
a, b, c = tup
b
5
tup = 4, 5, (6, 7)
a, b, (c, d) = tup
d
7
a, b = 1, 2
a
b
b, a = a, b
a
b
1
seq = [(1, 2, 3), (4, 5, 6), (7, 8, 9)]
for a, b, c in seq:
    print(f'a={a}, b={b}, c={c}')
a=1, b=2, c=3
a=4, b=5, c=6
a=7, b=8, c=9
values = 1, 2, 3, 4, 5
a, b, *rest = values
a
b
rest
[3, 4, 5]

3.3 리스트 (List)

크기 조절이 가능하고 내용을 변경할 수 있는 자료형입니다.

a, b, *_ = values
a = (1, 2, 2, 2, 3, 4, 2)
a.count(2)
4

3.4 리스트 (List)

가장 유연하게 사용할 수 있는 시퀀스 객체인 리스트의 다양한 메서드를 학습합니다.

a_list = [2, 3, 7, None]

tup = ("foo", "bar", "baz")
b_list = list(tup)
b_list
b_list[1] = "peekaboo"
b_list
['foo', 'peekaboo', 'baz']
gen = range(10)
gen
list(gen)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
b_list.append("dwarf")
b_list
['foo', 'peekaboo', 'baz', 'dwarf']
b_list.insert(1, "red")
b_list
['foo', 'red', 'peekaboo', 'baz', 'dwarf']
b_list.pop(2)
b_list
['foo', 'red', 'baz', 'dwarf']
b_list.append("foo")
b_list
b_list.remove("foo")
b_list
['red', 'baz', 'dwarf', 'foo']
"dwarf" in b_list
True
"dwarf" not in b_list
False
[4, None, "foo"] + [7, 8, (2, 3)]
[4, None, 'foo', 7, 8, (2, 3)]
x = [4, None, "foo"]
x.extend([7, 8, (2, 3)])
x
[4, None, 'foo', 7, 8, (2, 3)]
a = [7, 2, 5, 1, 3]
a.sort()
a
[1, 2, 3, 5, 7]
b = ["saw", "small", "He", "foxes", "six"]
b.sort(key=len)
b
['He', 'saw', 'six', 'small', 'foxes']
seq = [7, 2, 3, 7, 5, 6, 0, 1]
seq[1:5]
[2, 3, 7, 5]
seq[3:5] = [6, 3]
seq
[7, 2, 3, 6, 3, 6, 0, 1]
seq[:5]
seq[3:]
[6, 3, 6, 0, 1]

3.5 딕셔너리 (Dictionary)

키-값 쌍을 저장하는 유연한 해시 맵 자료형입니다.

seq[-4:]
seq[-6:-2]
[3, 6, 3, 6]
seq[::2]
[7, 3, 3, 0]
seq[::-1]
[1, 0, 6, 3, 6, 3, 2, 7]

3.6 사전 (Dictionary)

키-값 쌍을 저장하는 사전 자료형의 효율적인 사용법을 배웁니다.

empty_dict = {}
d1 = {"a": "some value", "b": [1, 2, 3, 4]}
d1
{'a': 'some value', 'b': [1, 2, 3, 4]}
d1[7] = "an integer"
d1
d1["b"]
[1, 2, 3, 4]
"b" in d1
True
d1[5] = "some value"
d1
d1["dummy"] = "another value"
d1
del d1[5]
d1
ret = d1.pop("dummy")
ret
d1
{'a': 'some value', 'b': [1, 2, 3, 4], 7: 'an integer'}
list(d1.keys())
list(d1.values())
['some value', [1, 2, 3, 4], 'an integer']
list(d1.items())
[('a', 'some value'), ('b', [1, 2, 3, 4]), (7, 'an integer')]
d1.update({"b": "foo", "c": 12})
d1
{'a': 'some value', 'b': 'foo', 7: 'an integer', 'c': 12}
tuples = zip(range(5), reversed(range(5)))
tuples
mapping = dict(tuples)
mapping
{0: 4, 1: 3, 2: 2, 3: 1, 4: 0}
words = ["apple", "bat", "bar", "atom", "book"]
by_letter = {}

for word in words:
    letter = word[0]
    if letter not in by_letter:
        by_letter[letter] = [word]
    else:
        by_letter[letter].append(word)

by_letter
{'a': ['apple', 'atom'], 'b': ['bat', 'bar', 'book']}

3.7 세트 (Set)

순서가 없고 중복된 원소를 허용하지 않는 자료형입니다.

by_letter = {}
for word in words:
    letter = word[0]
    by_letter.setdefault(letter, []).append(word)
by_letter
{'a': ['apple', 'atom'], 'b': ['bat', 'bar', 'book']}
from collections import defaultdict
by_letter = defaultdict(list)
for word in words:
    by_letter[word[0]].append(word)
hash("string")
hash((1, 2, (2, 3)))
hash((1, 2, [2, 3])) # 리스트는 가변형이므로 실패함
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[46], line 3
      1 hash("string")
      2 hash((1, 2, (2, 3)))
----> 3 hash((1, 2, [2, 3])) # 리스트는 가변형이므로 실패함

TypeError: unhashable type: 'list'
d = {}
d[tuple([1, 2, 3])] = 5
d
{(1, 2, 3): 5}
set([2, 2, 2, 1, 3, 3])
{2, 2, 2, 1, 3, 3}
{1, 2, 3}
a = {1, 2, 3, 4, 5}
b = {3, 4, 5, 6, 7, 8}
a.union(b)
a | b
{1, 2, 3, 4, 5, 6, 7, 8}
a.intersection(b)
a & b
{3, 4, 5}
c = a.copy()
c |= b
c
d = a.copy()
d &= b
d
{3, 4, 5}
my_data = [1, 2, 3, 4]
my_set = {tuple(my_data)}
my_set
{(1, 2, 3, 4)}
a_set = {1, 2, 3, 4, 5}
{1, 2, 3}.issubset(a_set)
a_set.issuperset({1, 2, 3})
True
{1, 2, 3} == {3, 2, 1}
True
sorted([7, 1, 2, 6, 0, 3, 2])
sorted("horse race")
[' ', 'a', 'c', 'e', 'e', 'h', 'o', 'r', 'r', 's']
seq1 = ["foo", "bar", "baz"]
seq2 = ["one", "two", "three"]
zipped = zip(seq1, seq2)
list(zipped)
[('foo', 'one'), ('bar', 'two'), ('baz', 'three')]
seq3 = [False, True]
list(zip(seq1, seq2, seq3))
[('foo', 'one', False), ('bar', 'two', True)]
for index, (a, b) in enumerate(zip(seq1, seq2)):
    print(f"{index}: {a}, {b}")
0: foo, one
1: bar, two
2: baz, three
list(reversed(range(10)))
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

3.8 리스트, 집합, 사전 컴프리헨션

파이썬다운 코드를 만드는 컴프리헨션 문법을 익힙니다.

strings = ["a", "as", "bat", "car", "dove", "python"]
[x.upper() for x in strings if len(x) > 2]
['BAT', 'CAR', 'DOVE', 'PYTHON']
unique_lengths = {len(x) for x in strings}
unique_lengths
{1, 2, 3, 4, 6}
set(map(len, strings))
{1, 2, 3, 4, 6}
loc_mapping = {value: index for index, value in enumerate(strings)}
loc_mapping
{'a': 0, 'as': 1, 'bat': 2, 'car': 3, 'dove': 4, 'python': 5}
all_data = [["John", "Emily", "Michael", "Mary", "Steven"],
            ["Maria", "Juan", "Javier", "Natalia", "Pilar"]]
names_of_interest = []
for names in all_data:
    enough_as = [name for name in names if name.count("a") >= 2]
    names_of_interest.extend(enough_as)
names_of_interest
['Maria', 'Natalia']
result = [name for names in all_data for name in names
          if name.count("a") >= 2]
result
['Maria', 'Natalia']
some_tuples = [(1, 2, 3), (4, 5, 6), (7, 8, 9)]
flattened = [x for tup in some_tuples for x in tup]
flattened
[1, 2, 3, 4, 5, 6, 7, 8, 9]
flattened = []

for tup in some_tuples:
    for x in tup:
        flattened.append(x)
[[x for x in tup] for tup in some_tuples]
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
def my_function(x, y):
    return x + y
my_function(1, 2)
result = my_function(1, 2)
result
3
def function_without_return(x):
    print(x)

result = function_without_return("hello!")
print(result)
hello!
None
def my_function2(x, y, z=1.5):
    if z > 1:
        return z * (x + y)
    else:
        return z / (x + y)
my_function2(5, 6, z=0.7)
my_function2(3.14, 7, 3.5)
my_function2(10, 20)
45.0
a = []
def func():
    for i in range(5):
        a.append(i)
func()
a
func()
a
[0, 1, 2, 3, 4, 0, 1, 2, 3, 4]
a = None
def bind_a_variable():
    global a
    a = []
bind_a_variable()
print(a)
[]
states = ["   Alabama ", "Georgia!", "Georgia", "georgia", "FlOrIda",
          "south   carolina##", "West virginia?"]
import re

def clean_strings(strings):
    result = []
    for value in strings:
        value = value.strip()
        value = re.sub("[!#?]", "", value)
        value = value.title()
        result.append(value)
    return result
clean_strings(states)
['Alabama',
 'Georgia',
 'Georgia',
 'Georgia',
 'Florida',
 'South   Carolina',
 'West Virginia']
def remove_punctuation(value):
    return re.sub("[!#?]", "", value)

clean_ops = [str.strip, remove_punctuation, str.title]

def clean_strings(strings, ops):
    result = []
    for value in strings:
        for func in ops:
            value = func(value)
        result.append(value)
    return result
clean_strings(states, clean_ops)
['Alabama',
 'Georgia',
 'Georgia',
 'Georgia',
 'Florida',
 'South   Carolina',
 'West Virginia']
for x in map(remove_punctuation, states):
    print(x)
   Alabama 
Georgia
Georgia
georgia
FlOrIda
south   carolina
West virginia
def short_function(x):
    return x * 2

equiv_anon = lambda x: x * 2
def apply_to_list(some_list, f):
    return [f(x) for x in some_list]

ints = [4, 0, 1, 5, 6]
apply_to_list(ints, lambda x: x * 2)
[8, 0, 2, 10, 12]
strings = ["foo", "card", "bar", "aaaa", "abab"]
strings.sort(key=lambda x: len(set(x)))
strings
['aaaa', 'foo', 'abab', 'bar', 'card']
some_dict = {"a": 1, "b": 2, "c": 3}
for key in some_dict:
    print(key)
a
b
c
dict_iterator = iter(some_dict)
dict_iterator
<dict_keyiterator at 0x10469a700>
list(dict_iterator)
['a', 'b', 'c']
def squares(n=10):
    print(f"Generating squares from 1 to {n ** 2}")
    for i in range(1, n + 1):
        yield i ** 2
gen = squares()
gen
<generator object squares at 0x104636260>
for x in gen:
    print(x, end=" ")
Generating squares from 1 to 100
1 4 9 16 25 36 49 64 81 100 
gen = (x ** 2 for x in range(100))
gen
<generator object <genexpr> at 0x10461e400>
sum(x ** 2 for x in range(100))
dict((i, i ** 2) for i in range(5))
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
import itertools
def first_letter(x):
    return x[0]

names = ["Alan", "Adam", "Wes", "Will", "Albert", "Steven"]

for letter, names in itertools.groupby(names, first_letter):
    print(letter, list(names)) # names is a generator
A ['Alan', 'Adam']
W ['Wes', 'Will']
A ['Albert']
S ['Steven']
float("1.2345")
float("something")
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[98], line 2
      1 float("1.2345")
----> 2 float("something")

ValueError: could not convert string to float: 'something'
def attempt_float(x):
    try:
        return float(x)
    except:
        return x
attempt_float("1.2345")
attempt_float("something")
'something'
float((1, 2))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[101], line 1
----> 1 float((1, 2))

TypeError: float() argument must be a string or a real number, not 'tuple'
def attempt_float(x):
    try:
        return float(x)
    except ValueError:
        return x
attempt_float((1, 2))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[103], line 1
----> 1 attempt_float((1, 2))

Cell In[102], line 3, in attempt_float(x)
      1 def attempt_float(x):
      2     try:
----> 3         return float(x)
      4     except ValueError:
      5         return x

TypeError: float() argument must be a string or a real number, not 'tuple'
def attempt_float(x):
    try:
        return float(x)
    except (TypeError, ValueError):
        return x
path = "examples/segismundo.txt"
f = open(path, encoding="utf-8")
lines = [x.rstrip() for x in open(path, encoding="utf-8")]
lines
['Sueña el rico en su riqueza,',
 'que más cuidados le ofrece;',
 '',
 'sueña el pobre que padece',
 'su miseria y su pobreza;',
 '',
 'sueña el que a medrar empieza,',
 'sueña el que afana y pretende,',
 'sueña el que agravia y ofende,',
 '',
 'y en el mundo, en conclusión,',
 'todos sueñan lo que son,',
 'aunque ninguno lo entiende.',
 '']
f.close()
with open(path, encoding="utf-8") as f:
    lines = [x.rstrip() for x in f]
f1 = open(path)
f1.read(10)
f2 = open(path, mode="rb")  # Binary mode
f2.read(10)
b'Sue\xc3\xb1a el '
f1.tell()
f2.tell()
10
import sys
sys.getdefaultencoding()
'utf-8'
f1.seek(3)
f1.read(1)
f1.tell()
5
f1.close()
f2.close()
path

with open("tmp.txt", mode="w") as handle:
    handle.writelines(x for x in open(path) if len(x) > 1)

with open("tmp.txt") as f:
    lines = f.readlines()

lines
['Sueña el rico en su riqueza,\n',
 'que más cuidados le ofrece;\n',
 'sueña el pobre que padece\n',
 'su miseria y su pobreza;\n',
 'sueña el que a medrar empieza,\n',
 'sueña el que afana y pretende,\n',
 'sueña el que agravia y ofende,\n',
 'y en el mundo, en conclusión,\n',
 'todos sueñan lo que son,\n',
 'aunque ninguno lo entiende.\n']
import os
os.remove("tmp.txt")
with open(path) as f:
    chars = f.read(10)

chars
len(chars)
10
with open(path, mode="rb") as f:
    data = f.read(10)

data
b'Sue\xc3\xb1a el '
data.decode("utf-8")
data[:4].decode("utf-8")
---------------------------------------------------------------------------
UnicodeDecodeError                        Traceback (most recent call last)
Cell In[118], line 2
      1 data.decode("utf-8")
----> 2 data[:4].decode("utf-8")

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc3 in position 3: unexpected end of data
sink_path = "sink.txt"
with open(path) as source:
    with open(sink_path, "x", encoding="iso-8859-1") as sink:
        sink.write(source.read())

with open(sink_path, encoding="iso-8859-1") as f:
    print(f.read(10))
Sueña el r
os.remove(sink_path)
f = open(path, encoding='utf-8')
f.read(5)
f.seek(4)
f.read(1)
f.close()
---------------------------------------------------------------------------
UnicodeDecodeError                        Traceback (most recent call last)
Cell In[121], line 4
      2 f.read(5)
      3 f.seek(4)
----> 4 f.read(1)
      5 f.close()

File <frozen codecs>:322, in decode(self, input, final)

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb1 in position 0: invalid start byte