파이썬은 이미 핵심 엔진에서 dictionary를 이용하여 언어를 구성하고 있습니다.
print(builtins.dict)
위 소스코드를 돌려보면 무수히 많은 함수의 이름들이 dictionary의 keys:values로 구성된 모습을 확인 할 수 있어요.
그럼 중복된 값들도 있을텐데 어떻게 구분할까요? 바로 헤시테이블(hashtable)
을 이용하여 적은 자원으로 효율적인 결과를 도출하는 거에요.
그래서 dictionary는-> Key 중복 허용 X, Set -> 중복 허용X
print('ex1-1')
print(__builtins__.__dict__)
t1 = (10, 20, (30,40,50))
t2 = (10, 20, [30,40,50]) # 리스트가 있는 t2는 hash화 할수 없음.
print('ex1-2',hash(t1))
# print('ex1-3',hash(t2))
마지막 프린트문은 아래와 같이 오류가 남.
TypeError: unhashable type: 'list'
import csv
with open('./statecode.csv','r',encoding='UTF-8') as f:
temp = csv.reader(f)
# Header skip
next(temp)
# 변환
NA_CODES = [tuple(x) for x in temp]
print('ex2-1',)
print(NA_CODES)
n_code1 = {country:code for code, country in NA_CODES}
n_code2 = {country.upper():code for code, country in NA_CODES}
print()
print()
print('ex2-2',)
print(n_code1)
print(n_code2)
source = (
('k1','val1'),
('k1','val2'),
('k2','val3'),
('k2','val4'),
('k2','val5'),
)
new_dict1 = {}
for k, v in source:
if k in new_dict1:
new_dict1[k].append(v)
else:
new_dict1[k] = [v]
print('ex3-1 - ', new_dict1)
new_dict2 = {}
# Use setdefault
for k,v in source:
new_dict2.setdefault(k, []).append(v)
print('ex3-2 - ', new_dict2)
output
ex3-1 - {'k1': ['val1', 'val2'], 'k2': ['val3', 'val4', 'val5']}
ex3-2 - {'k1': ['val1', 'val2'], 'k2': ['val3', 'val4', 'val5']}
이중 튜플 값을 통해서 --> dictionary를 만들건데요.
이를 위해 source 변수를 위와 같이 처음 만들어 볼게요.
dictionary의 특징을 짚고 넘어가면 key값은 중복되면 안된다고 했조?
근데 잘 보면 k1이 2개 k2가 3개나 있네요. 일단 계속 진행해 볼게요.
빈 dictionary 변수 생성을 위해 new_dict1을 만들어요.
for문을 돌려서 한번 진행해보지요.
k,v로 로컬 변수를 만들고 k에 new_dict1값이 있으면 기존 key값
이
append()를 사용해서 추가하는 방식이라는 말이조.new_dict1[k].append(v)
그럼 else는 그 반대로 해당 값이 없는 경우 딕셔너리의 key를 생성하고 값을 할당해서 만든다는 거조?!
위 설명의 떡밥은 모두 setdefault를 설명하기 위함입니다.
빈 딕셔너리 new_dict2를 만들게요.
for문 만들고, 여기서 중요한 것이 위 코드처럼 if, else문으로 할 필요 없이 이미 구현된 setdefault
메소드로 편안하게~ 만들어보조.
즉, 천번째 인자는 k (key값)을 넣으면서 value값을 append하고! key 값이 없는 경우 빈 []를 만드어 새로운 key를 만든 다음 따라오는 value를 넣어주는 방식으로 key-value pair를 새롭게 만들어 가는거조.
class UserDict(dict):
def __missing__(self, key):
print('Called : __missing__')
if isinstance(key, str):
raise KeyError(key)
return self[str(key)]
def get(self, key, default=None):
print('Called : __getitem__')
try:
return self[key]
except KeyError:
return default
def __contains__(self, key):
print('Called : __containes__')
return key in self.keys() or str(key in self.keys())
user_dict1 = UserDict(one=1, two=2)
user_dict2 = UserDict({'one':1, 'two':2})
user_dict3 = UserDict([('one',1), ('two',2)])
# 출력
print('ex4-1 -',user_dict1,user_dict2,user_dict3)
print('ex4-2 -',user_dict2.get('two'))
print('ex4-3 -', 'one' in user_dict3)
# print('ex4-4 -', user_dict3['three'])
# print('ex4-5 -', user_dict3.get('three'))
print('ex4-6 -', 'three' in user_dict3)
print()
print()
요녀석은 찬찬히 봐야하는 녀석인데 우리가 사용하는 파이썬 딕셔너리가 어떻게 움직이는지 알기위해 만든 클래스에요.
get() 메서드를 호출 할때 선언된 get()메소드의 동작이 어떻게 되는지.
in 키워드로 값이 있는지 체크할때 선언된 __contains__를 통해서 해당 값이 dictionary에 있는지 없는지 체크하는 메커니즘과
만약 없다면 __missing__()메소드가 호출되는 과정을 보여주기 위함이에요.
ex4-1 - {'one': 1, 'two': 2} {'one': 1, 'two': 2} {'one': 1, 'two': 2}
Called : __getitem__
ex4-2 - 2
Called : __containes__
ex4-3 - True
Called : __containes__
ex4-6 - True
from types import MappingProxyType
d = {'key1': 'TEST1'}
# Read Only
d_frozen = MappingProxyType(d)
print('ex5-1 -', d, id(d))
print('ex5-2 -', d_frozen, id(d_frozen))
print('ex5-3 -', d is d_frozen, d == d_frozen) # 전자 ID, 후자 값이 같은지
# 추가 & 수정 불가
# d_frozen['key1'] = 'TEST2'
# 'mappingproxy' object does not support item assignment
d['key2'] = 'TEST2'
print('ex5-4 -', d)
여기서는 dictionary의 값을 변동 할 수 없게 하는 방법을 볼건데요.
일단 MappingProxyType 클래스를 통해서 dictionary
를 불변으로 만들수 있어요.
특징:
여기서 포인트가 d_frozen의 키값을 'TEST2'로 변동하려고 하면
mappingproxy' object does not support item assignment
라는 오류가 발생하는걸 확인 할 수 있습니다. 즉. 값을 할당 할수 없다는 거조.
output
ex5-1 - {'key1': 'TEST1'} 1385814807872
ex5-2 - {'key1': 'TEST1'} 1385814662016
ex5-3 - False True
ex5-4 - {'key1': 'TEST1', 'key2': 'TEST2'}
s1 = {"apple",'orange', 'apple','orange','kiwii'}
s2 = set(["apple",'orange', 'apple','orange','kiwii'])
s3 = {3}
s4 = set() # Not {}, 빈 중괄호로 선언 하면 Dictionary임
s5 = {}
print(type(s5))
s6 = frozenset({"apple",'orange', 'apple','orange','kiwii'})
# 추가
s1.add('melon')
# 추가 불가 - frozenset
# s6.add('melon')
# 오류 결과 : 'frozenset' object has no attribute 'add'
print('ex6-1 -', s1, type(s1))
print('ex6-2 -', s2, type(s2))
print('ex6-3 -', s3, type(s3))
print('ex6-4 -', s4, type(s4))
print('ex6-5 -', s5, type(s5))
print('ex6-6 -', s6, type(s6))
set은 중괄호를 통해서 만들어지는데요. 하지만 빈 {}기호로 선언하면 딕셔너리로 되니깐. 반드시 set()
메소드로 우항에 만들어야 set 객체 생성이 가능해요. 값을 넣어서 만들 경우는 물론 set 객체 생성이 가능하조.
눈여겨 봐야할 것은 s6 변수를 frozenset()를 이용해서 만들었는데요. 얼린셋
이라는 의미인 만큼 값을 추가, 변경 할 수 없음에 유의하세요.
그래서 add()를 이용해서 값을 추가하려니 해당 속성이 없다고 오류가 나타난걸 확인 할 수 있어요.
ex6-1 - {'kiwii', 'orange', 'melon', 'apple'} <class 'set'>
ex6-2 - {'kiwii', 'orange', 'apple'} <class 'set'>
ex6-3 - {3} <class 'set'>
ex6-4 - set() <class 'set'>
ex6-5 - {} <class 'dict'>
ex6-6 - frozenset({'kiwii', 'orange', 'apple'}) <class 'frozenset'>
dis 모듈을 통해서 bytecode 단위로 분해해 확인 가능한 모듈
아래 소스 코드를 보면 set에 리스트를 넣었는지 아닌지 그차이 밖에 없어요. 하지만 내부 로직 과정이 하나 더 생긴걸 확인 할 수 있어요.아~ 이런거구나 확인하시면 되요.
from dis import dis #
print(dis('{10}')) #
# 1 0 LOAD_CONST 0 (10)
# 2 BUILD_SET 1
# 4 RETURN_VALUE
print(dis('set([10])'))
# 1 0 LOAD_NAME 0 (set)
# 4 BUILD_LIST 1
# 6 CALL_FUNCTION 1
# 8 RETURN_VALUE
아래 소스코드를 통해서 유니코드를 어떻게 출력하는지 볼건데요.
요점은 그동안 배웠던 것처럼 리스트 컴프리헨션, 딕셔너리 컴프리헨션과 같이 set 컴프리헨션을 구현한다는 점이에요.
여기서 chr()메소드를 통해서 유니코드 기호를 확인 할 수 있어요.
해당 유니코드의 기호말고 이름을 확인 하고자 unicodedata 라이브러리의 name 메서드를 이용해서 해당 기호의 이름을 기호 그 자체가 아닌 영문으로 이름을 확인 해 볼 수 있다는 점이에요.
유니코드를 그대로 출력하지 않고 이름을 출력함
from unicodedata import name
print('ex7-1 -')
print({(chr(i), '') for i in range(0,256)})
print({name(chr(i), '') for i in range(0,256)})
# 바로 위 코드에서 눈여겨 봐야할 것은 중복된 값을 피하기 위해 {}로 감싸져 있기 때문에 중복을 피했다는거.