namedtuple
for lightweight, immutable data containers before classes#Named Tuple example
import collections
Grade = collections.namedtuple('Grade', ('score', 'weight'))
Notes:
namedtuple
classes.namedtuple
are accessible using indexes and iterationlist
type's sort
method takes optional key
argumentnames = ['Socrates', 'Archimedes', 'Plato', 'Aristotle']
names.sort(key=lambda x: -len(x))
print(names)
['Archimedes', 'Aristotle', 'Socrates', 'Plato']
__call__
method allows for classes to be called just like functionscallable
built-in function to return True for the instancefrom collections import defaultdict
class BetterCountMissing(object):
def __init__(self):
self.added = 0
def __call__(self):
self.added += 1
return 0
counter = BetterCountMissing()
counter()
assert callable(counter)
counter = BetterCountMissing()
current = {'greed': 12, 'blau': 5}
increments = [
('red', 2),
('greed', 23)
]
result = defaultdict(counter, current) # Relies on __call__
for key, amount in increments:
result[key] += amount
assert counter.added == 1
@classmethod
Polymorphism to Construct Objects Generically¶To see how we can bind classes together more cohesively, see the following example:
class GenericInputData(object):
def read(self):
raise NotImplementedError
@classmethod
def generate_inputs(cls, config):
raise NotImplementedError
class PathInputData(GenericInputData):
def __init__(self, path):
super().__init__()
self.path = path
def read(self):
return open(self.path).read()
@classmethod
def generate_inputs(cls, config):
data_dir = config['data_dir']
for name in os.listdir(data_dir):
yield cls(os.path.join(data_dir, name))
class GenericWorker(object):
# …
def map(self):
raise NotImplementedError
def reduce(self, other):
raise NotImplementedError
@classmethod
def create_workers(cls, input_class, config):
workers = []
for input_data in input_class.generate_inputs(config):
workers.append(cls(input_data))
return workers
def mapreduce(worker_class, input_class, config):
workers = worker_class.create_workers(input_class, config)
return execute(workers)
super
¶__init__
class MyBaseClass(object):
def __init__(self, value):
self.value = value
class MyChildClass(MyBaseClass):
def __init__(self):
MyBaseClass.__init__(self, 5)
class TimesTwo(object):
def __init__(self):
self.value *= 2
class PlusFive(object):
def __init__(self):
self.value += 5
class OneWay(MyBaseClass, TimesTwo, PlusFive):
def __init__(self, value):
MyBaseClass.__init__(self, value)
TimesTwo.__init__(self)
PlusFive.__init__(self)
foo = OneWay(5)
print('First ordering is (5 * 2) + 5 =', foo.value)
class AnotherWay(MyBaseClass, PlusFive, TimesTwo):
def __init__(self, value):
MyBaseClass.__init__(self, value)
TimesTwo.__init__(self)
PlusFive.__init__(self)
bar = AnotherWay(5)
print('Second ordering still is', bar.value)
First ordering is (5 * 2) + 5 = 15 Second ordering still is 15
Diamond inherticance happens when two parent classes have the same parent
class TimesFive(MyBaseClass):
def __init__(self, value):
MyBaseClass.__init__(self, value)
self.value *= 5
class PlusTwo(MyBaseClass):
def __init__(self, value):
MyBaseClass.__init__(self, value)
self.value += 2
class ThisWay(TimesFive, PlusTwo):
def __init__(self, value):
TimesFive.__init__(self, value)
PlusTwo.__init__(self, value)
foo = ThisWay(5)
print('Should be (5 * 5) + 2 = 27 but is', foo.value)
Should be (5 * 5) + 2 = 27 but is 7
Because PlusTwo.__init__
causes self.value to be reset back to 5 when MyBaseClass.__init__
gets called a second time.
class Explicit(MyBaseClass):
def __init__(self, value):
super(__class__, self).__init__(value * 2)
class Implicit(MyBaseClass):
def __init__(self, value):
super().__init__(value * 2)
assert Explicit(10).value == Implicit(10).value
#Use MRO method to see resolution order
class TimesFiveCorrect(MyBaseClass):
def __init__(self, value):
super(TimesFiveCorrect, self).__init__(value)
self.value *= 5
class PlusTwoCorrect(MyBaseClass):
def __init__(self, value):
super(PlusTwoCorrect, self).__init__(value)
self.value += 2
class GoodWay(TimesFiveCorrect, PlusTwoCorrect):
def __init__(self, value):
super(GoodWay, self).__init__(value)
foo = GoodWay(5)
print('Should be 5 * (5 + 2) = 35 and is ', foo.value)
Should be 5 * (5 + 2) = 35 and is 35
from pprint import pprint
pprint(GoodWay.mro())
[<class '__main__.GoodWay'>, <class '__main__.TimesFiveCorrect'>, <class '__main__.PlusTwoCorrect'>, <class '__main__.MyBaseClass'>, <class 'object'>]
__init__
to be called.class ToDictMixin(object):
def to_dict(self):
return self._traverse_dict(self.__dict__)
def _traverse_dict(self, instance_dict):
output = {}
for key, value in instance_dict.items():
output[key] = self._traverse(key, value)
return output
def _traverse(self, key, value):
if isinstance(value, ToDictMixin):
return value.to_dict()
elif isinstance(value, dict):
return self._traverse_dict(value)
elif isinstance(value, list):
return [self._traverse(key, i) for i in value]
elif hasattr(value, '__dict__'):
return self._traverse_dict(value.__dict__)
else:
return value
Following uses dynamic attribute access using hasattr
, dynamic type inspection with isinstance
and accessing the instance dictionary __dict__
.
class BinaryTree(ToDictMixin):
def __init__(self, value, left=None, right=None):
self.value = value
self.left = left
self.right = right
tree = BinaryTree(10,
left=BinaryTree(7, right=BinaryTree(9)),
right=BinaryTree(13, left=BinaryTree(11)))
print(tree.to_dict())
{'value': 10, 'left': {'value': 7, 'left': None, 'right': {'value': 9, 'left': None, 'right': None}}, 'right': {'value': 13, 'left': {'value': 11, 'left': None, 'right': None}, 'right': None}}
__
)class MyObject(object):
def __init__(self):
self.public_field = 5
self.__private_field = 10
def get_private_field(self):
return self.__private_field
foo = MyObject()
assert foo.public_field == 5
assert foo.get_private_field() == 10
foo.__private_field
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-7-1fc23a9aeac8> in <module>() 2 assert foo.public_field == 5 3 assert foo.get_private_field() == 10 ----> 4 foo.__private_field AttributeError: 'MyObject' object has no attribute '__private_field'
class MyOtherObject(object):
def __init__(self):
self.__private_field = 71
@classmethod
def get_private_field_of_instance(cls, instance):
return instance.__private_field
bar = MyOtherObject()
assert MyOtherObject.get_private_field_of_instance(bar) == 71
class MyParentObject(object):
def __init__(self):
self.__private_field = 71
class MyChildObject(MyParentObject):
def get_private_field(self):
return self.__private_field
baz = MyChildObject()
baz.get_private_field()
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-10-38fbdb394643> in <module>() 6 return self.__private_field 7 baz = MyChildObject() ----> 8 baz.get_private_field() <ipython-input-10-38fbdb394643> in get_private_field(self) 4 class MyChildObject(MyParentObject): 5 def get_private_field(self): ----> 6 return self.__private_field 7 baz = MyChildObject() 8 baz.get_private_field() AttributeError: 'MyChildObject' object has no attribute '_MyChildObject__private_field'
_
) should be accessed with caution._classname__privatevar
collections.abc
for Custom Container Types¶class FrequencyList(list):
def __init__(self, members):
super().__init__(members)
def frequency(self):
counts = {}
for item in self:
counts.setdefault(item, 0)
counts[item] += 1
return counts
__getitem__
to take advantage of list
-like featuresclass BinaryNode(object):
def __init__(self, value, left=None, right=None):
self.value = value
self.left = left
self.right = right
class IndexableNode(BinaryNode):
def _search(self, count, index):
# …
# Returns (found, count)
return
def __getitem__(self, index):
found, _ = self._search(0, index)
if not found:
raise IndexError('Index out of range')
return found.value
tree = IndexableNode(
10,
left=IndexableNode(
5,
left=IndexableNode(2),
right=IndexableNode(
6, right=IndexableNode(7))),
right=IndexableNode(
15, left=IndexableNode(11)))
print('LRR =', tree.left.right.right.value)
#print('Index 0 =', tree[0])
#print('Index 1 =', tree[1])
#print('11 in the tree?', 11 in tree)
#print('17 in the tree?', 17 in tree)
#print('Tree is', list(tree))
LRR = 7