<jupyter_start><jupyter_text>An object is hashtable if it has a hash value which never changes during its lifetime(it needs a __hash__() method), and can be compared to other objects (it needs an __eq__() method).  All of python's immutable built-in objects are hashable.<jupyter_code>tt = (1, 2, (30, 40))
hash(tt)
tl = (1, 2, [30, 40])
hash(tl)
tf = (1, 2, frozenset([30, 40]))
hash(tf)
# the various means of building a dict
a = dict(one=1, two=2, three=3)
b = {'one':1, 'two':2, 'three':3}
c = dict(zip(['one', 'two', 'three'], [1, 2, 3]))
d = dict([('two', 2), ('one', 1), ('three', 3)])
e = dict({'three':3, 'two':2, 'one':1})
a == b == c == d == e
DIAL_CODES = [
    (86, 'China'),
    (91, 'India'),
    (1, 'United States'),
    (62, 'Indonesia'),
    (55, 'Brazil'),
    (92, 'Pakistan'),
    (880,'Bangladesh'),
    (234,'Nigeria'),
    (7,  'Russia'),
    (81, 'Japan'),
]
country_code = {country: code for code, country in DIAL_CODES}
country_code
{code: country.upper() for country, code in country_code.items() if code < 66}
#index0.py
'''Build an index mapping word -> list of occurrences'''
import sys
import re

WORD_RE = re.compile('\w+') 
index = {}
with open('test', encoding='utf-8') as fp:
    for line_no, line in enumerate(fp, 1):   #enumerate(seq, [start=0])
        for match in WORD_RE.finditer(line): 
            word = match.group()
            column_no = match.start() + 1
            location = (line_no, column_no)
            occurrences = index.get(word, []) #set [] if not found
            occurrences.append(location)  
            index[word] = occurrences
            
for word in sorted(index, key=str.upper):
    print(word, index[word])
#index.py uses dict.setdefault 
import sys 
import re

WORD_RE = re.compile('\w+')

index = {}
with open('test', encoding='utf-8') as fp:
    for line_no, line in enumerate(fp, 1):
        for match in WORD_RE.finditer(line):
            word = match.group()
            column_no = match.start() + 1
            location = (line_no, column_no)
            index.setdefault(word, []).append(location)
            '''
            #this last line is the same as running ...
            if key not in my_dict:
                my_dict[key] = []
            my_dict[key].append(new_value)
            '''
            
for word in sorted(index, key=str.upper):
    print(word, index[word])
#defaultdict: another take on missing keys
#collections.defaultdict 
'''
if 'new-key' is not in dd(defaultdict) then the expression dd['new-key'] does the following steps:
1. calls list() to create a new list
2. inserts the list into dd using 'new-key' as key
3. returns s reference to that list
'''
import sys
import re
import collections

WORD_RE = re.compile('\w+')

index = collections.defaultdict(list)
with open('test', encoding='utf-8') as fp:
    for line_no, line in enumerate(fp, 1):
        for match in WORD_RE.finditer(line):
            word = match.group()
            column_no = match.start() + 1
            location = (line_no, column_no)
            index[word].append(location)

for word in sorted(index, key=str.upper):
    print(word, index[word])<jupyter_output>a [(1, 13)]
heng [(1, 5)]
Heng [(2, 5)]
I [(3, 1)]
is [(1, 10), (2, 21)]
jet [(2, 24)]
lin [(1, 1), (2, 28)]
Lin [(2, 1)]
love [(3, 3)]
nickname [(2, 12)]
s [(2, 10)]
worker [(1, 15)]
you [(3, 8)]<jupyter_text>The \_\_missing\_\_ method> Mapping deal with missing key is the aptly named \_\_missing\_\_ method.> the standard dict.\_\_getitem\_\_ will call it whenever a key is not found,> instead of raising KeyError.<jupyter_code>#implements a class StrKeyDict0 
class StrKeyDict0(dict):  #StrKeyDict0 inherits from dict.
    
    def __missing__(self, key):
        if isinstance(key, str):  #Check whether key is already a str. If it is, and it's missing, raise KeyError.
            raise KeyError(key)
        return self[str(key)]  #Build str from key and look it up
    
    def get(self, key, default=None):
        try:
            return self[key]  #
        except KeyError:
            return default
    
    def __contain__(self, key):
        return str(key) in self.keys() or key in self.keys()

dd = StrKeyDict0([('2', 'two'), ('4', 'four')])
dd.keys()
dd[2]
dd['2']
dd.get(4)
dd.get(1, 'N/A')
'2' in dd
1 in dd
type(dd)
'2' in dd<jupyter_output><empty_output><jupyter_text>collections.OrderedDict<jupyter_code>from collections import OrderedDict

d = {'apple':3, 'banana':6, 'pear':2, 'orange':1}
OrderedDict(sorted(d.items(), key=lambda t: t[0])) #sort by key
OrderedDict(sorted(d.items(), key=lambda t: t[1])) #sort by value
OrderedDict(sorted(d.items(), key=lambda t: len(t[0])))  #sort by length of key
d = OrderedDict.fromkeys('abcde')
d
d.move_to_end('b')
''.join(d.keys())
class LastUpdateOrderedDict(OrderedDict):
   #store items in the order the keys were last added.

    def __setitem__(self, key, value):
        if key in self:
            del self[key]
        OrderedDict.__setitem__(self, key, value)

d = {'apple':3, 'banana':6, 'pear':2, 'orange':1}
luod = LastUpdateOrderedDict(sorted(d.items(), key=lambda t: t[0]))
luod['apple'] = 4
luod
#collections.ChainMap
from collections import ChainMap
import builtins
pylookup = ChainMap(locals(), globals())
#collections.Counter
from collections import Counter
ct = collections.Counter('abracadabra')
ct
ct.update('aaaaazzz')
ct
ct.most_common(2)<jupyter_output><empty_output><jupyter_text>Subclassing UserDict<jupyter_code>import collections

class StrKeyDict(collections.UserDict):
    
    # key not in dict
    def __missing__(self, key):
        if isinstance(key, str):
            raise KeyError(key)
        return self[str(key)]
    
    # key in dict
    def __contains__(self, key):
        return str(key) in self.data
    
    # d[key] = item
    def __setitem__(self, key, item):
        self.data[str(key)] = item
        
d = StrKeyDict()
d[1] = '1'
d<jupyter_output><empty_output><jupyter_text>Immutable mappings<jupyter_code>from types import MappingProxyType
d = {1: 'A'}
d_proxy = MappingProxyType(d)
d_proxy
d_proxy[1]
d_proxy
d = {1: 'A', 2: 'B'}
d_proxy<jupyter_output><empty_output><jupyter_text>Sets<jupyter_code>l = ['spam', 'spam', 'eggs', 'spam']
set(l)  #removing duplication
list(set(l))  #set element must be hashable
a = set(['a', 'b', 'c', 'd'])
b = set(['d', 'e', 'f'])
a | b
a & b
a - b
a.intersection(b)
s = {1}
type(s)
s
s.pop()
s
frozenset(range(10))
from unicodedata import name
{chr(i) for i in range(32, 256) if 'SIGN' in name(chr(i), '')}
name(chr(35), '')<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.15744551135497636, 'fraction_numerical': 0.022862368541380886, 'mean_word_length': 3.418855218855219, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 28, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '6', 'dir': 'jupyter-structured-clean-dedup', 'id': '282440', 'license': 'Apache-2.0', 'n_tokens_mistral': 2443, 'n_tokens_neox': 2335, 'n_words': 697, 'path': 'notebook/fluent_ch3.ipynb', 'repo_name': 'Lin0818/py-study-notebook', 'size': '6561'}