Skip to content

Instantly share code, notes, and snippets.

@dutc
Created March 24, 2021 23:35
Show Gist options
  • Save dutc/4e5aa3136c893182a620f8e680d8d4ab to your computer and use it in GitHub Desktop.
Save dutc/4e5aa3136c893182a620f8e680d8d4ab to your computer and use it in GitHub Desktop.
“Python Expert” Newsletter (Mar 23, 2021): Learning Corner, Python Data Structure Fundamentals and “Concordance”
from collections.abc import Mapping, MutableMapping
class cidict(dict):
''' a case insensitive, immutable `dict` subclass '''
def __init__(self, *args, **kwargs):
super().__init__({k.lower(): v for k, v in dict(*args, **kwargs).items()})
def __missing__(self, key):
return self[key.lower()]
__setitem__ = __delitem__ = None
clear = update = pop = popitem = setdefault = update = None
d = cidict({
'abc': 123,
'XYZ': 456,
})
assert d['abc'] == d['ABC']
assert d['xyz'] == d['XYZ']
## if immutable…
class cidict(Mapping):
''' a case insensitive, immutable `dict` subclass '''
def __init__(self, *args, **kwargs):
self._data = {k.lower(): v for k, v in dict(*args, **kwargs).items()}
def __getitem__(self, key):
return self._data[key.lower()]
def __iter__(self):
return iter(self._data)
def __len__(self):
return len(self._data)
d = cidict({
'abc': 123,
'XYZ': 456,
})
assert d['abc'] == d['ABC']
assert d['xyz'] == d['XYZ']
## if mutable…
class cidict(MutableMapping):
''' a case insensitive, mutable `dict` subclass '''
def __init__(self, *args, **kwargs):
self._data = {k.lower(): v for k, v in dict(*args, **kwargs).items()}
def __getitem__(self, key):
return self._data[key.lower()]
def __setitem__(self, key, value):
self._data[key.lower()] = value
def __delitem__(self, key):
del self._data[key.lower()]
def __iter__(self):
return iter(self._data)
def __len__(self):
return len(self._data)
d = cidict({
'abc': 123,
'XYZ': 456,
})
d['DEF'] = 789
d.update({'GHI': 321})
assert d['abc'] == d['ABC']
assert d['def'] == d['DEF']
assert d['xyz'] == d['XYZ']
assert d['ghi'] == d['GHI']
#!/usr/bin/env python3
class customdict(dict):
def __init__(self, factory, *args, **kwargs):
self.factory = factory
super().__init__(*args, **kwargs)
def __missing__(self, key):
rv = self[key] = self.factory(key)
return rv
d = customdict(lambda _: None)
assert "abc" not in d
print(f'{d["abc"] = }')
assert "abc" in d
#!/usr/bin/env python3
from collections import OrderedDict
from io import StringIO
from json import load, dump
## `collections.OrderedDict` uses ordering as part of its consideration of equality…
od1 = OrderedDict({'one': 1, 'two': 2, 'three': 3})
od2 = OrderedDict({'three': 3, 'two': 2, 'one': 1})
assert od1 != od2
d1 = {'one': 1, 'two': 2, 'three': 3}
d2 = {'three': 3, 'two': 2, 'one': 1}
assert d1 == d2
## this may be relevant when working with JSON (or XML) data where you want to preserve ordering while encoding the data using Python mapping types…
json_data1 = StringIO('{"one": 1, "two": 2, "three": 3}')
json_data2 = StringIO('{"three": 3, "two": 2, "one": 1}')
data1 = load(json_data1)
data2 = load(json_data2)
dump(data1, file1 := StringIO())
dump(data2, file2 := StringIO())
assert data1 == data2
assert file1.getvalue() != file2.getvalue()
## vs…
json_data1 = StringIO('{"one": 1, "two": 2, "three": 3}')
json_data2 = StringIO('{"three": 3, "two": 2, "one": 1}')
data1 = load(json_data1, object_hook=OrderedDict)
data2 = load(json_data2, object_hook=OrderedDict)
dump(data1, file1 := StringIO())
dump(data2, file2 := StringIO())
assert data1 != data2
assert file1.getvalue() != file2.getvalue()
#!/usr/bin/env python3
class customdict(dict):
def __init__(self, factory, *args, **kwargs):
self.factory = factory
super().__init__(*args, **kwargs)
def __missing__(self, key):
rv = self[key] = self.factory(key)
return rv
class overridedict(customdict):
def __init__(self, *args, **kwargs):
super().__init__(lambda k: k, *args, **kwargs)
overrides = overridedict({
'abc': 'ABC',
})
print(f'{overrides["abc"] = }')
print(f'{overrides["def"] = }')
print(f'{overrides["xyz"] = }')
# NOTE: ignoring the statefulness of `__missing__` the above is roughly
# equivalent to…
overrides = {
'abc': 'ABC',
}
print(f'{overrides.get(k := "abc", k) = }')
print(f'{overrides.get(k := "def", k) = }')
print(f'{overrides.get(k := "xyz", k) = }')
# QUESTION: when do you use `collections.defaultdict.__getitem__` and when do you
# use `dict.get`?
# QUESTION: when do you make a `collections.defaultdict` “decay” into a
# regular `dict` by setting `d.default_factory = None`
from collections import defaultdict
d = defaultdict(int)
print(f'{d["abc"] = }')
d.default_factory = None
try:
print(f'{d["xyz"] = }')
except KeyError as e:
key, = e.args
print(f'Not found: {key!r}')
@dutc
Copy link
Author

dutc commented Mar 24, 2021

For the full write-up and discussion, sign up for the “Python Expert” newsletter!

bit.ly/expert-python

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment