Skip to content

Instantly share code, notes, and snippets.

@Smarker
Last active August 13, 2018 22:18
Show Gist options
  • Save Smarker/ba95947687c7684280c15c43eca0bce3 to your computer and use it in GitHub Desktop.
Save Smarker/ba95947687c7684280c15c43eca0bce3 to your computer and use it in GitHub Desktop.
#user:smarker #python

Fluent Python

Dunder Methods

representation methods
string/bytes representation __repr__, __str__, __format__, __bytes__
conversion to number __abs__, __bool__, __complex__, __int__, __float__, __hash__, __index__
emulating collections __len__, __getitem__, __setitem__, __delitem__, __contains__
iteration __iter__, __reversed__, __next__
emulating callables __call__
context management __enter__, __exit__
instance creation and destruction __new__, __init__, __del__
attribute management __getattr__, __getattribute__, __setattr__, __delattr__, __dir__
attribute descriptors __get__, __set__, __delete__
class services __prepare__, __instancecheck__, __subclasscheck__
unary numeric operators __neg__, -, __pos__, +, __abs__
rich compartison operators __lt__, >, __le__, <=, __eq__, ==, __ne__, !=, __gt__, >, __ge__, >=
arithmetic operators __add__, +, __sub__, -, __mul__, *,__truediv__, /,__floordiv__,//,__mod__, %, __divmod__, __pow__ **, __round__
reversed arithmetic operators __radd__, __rsub__, __rmul__, __rtruediv__, __rfloordiv__, __rmod__, __rdivmod__, __rpow__
arithmetic operators __iadd__, __isub__, __imul__, __itruediv__, __ifloordiv__, __imod__, __ipow__
bitwise operators __invert__, ~, __lshift__, <<, __rshift__, >>, __and__, &, __or__, `
reversed bitwise operators __rlshift__, __rrshift__, __rand__, __rxor__, __ror__

Definitions

  • Python Data Model
    • describes python as a framework
    • enables custom objects to interact with basic language constructs
  • Dunder methods - __getitem__

Comparisons

__repr__ __str__
unambigous as it gets the standard representation of the object ambiguous as gets the string representation (lose info)
%r %s
called if __str__ is not implemented by the class
Container sequences Flat sequences
list, tuple and collections.deque str, bytes, bytearray, memoryview, array.array
can hold items of many different types hold items of one type
hold references to objects they contain physically store the value of the items within its memory space
mutable immutable

Advice

  • Implement __repr__ for any class you implement. This should be second nature. Implement __str__ if you think it would be useful to have a string version which errs on the side of more readability in favor of more ambiguity
  • Can use genexp to save memory because it yields items one by one using the iterator protocol instead of building a whole list
  • using a tuple as a collection of fields -> the number of items is often fixed and their order is always vital
  • Putting mutable items in tuples is not a good idea
  • functions or methods that change an object in-place should return None to make it clear to the caller that the object itself was changed, and no new object was created

Pythonic Card Deck

  • No need to implement random card method, as random function already exists (from random import choice)
  • implement __setitem__ to change immutable objects
import collections

Shirt = collections.namedtuple('Shirt', ['size', 'color'])

class ShirtCollection:
    sizes = list('SML')
    colors = 'red blue green'.split()

    def __init__(self):
        self._shirts = [ Shirt(size, color) for size in self.sizes
                                            for color in self.colors]
    def __len__(self):
      return len(self._shirts)

    def __getitem__(self, position):
        return self._shirts[position]

    def __setitem__(self, position, item):
        self._shirts[position] = item
  • __repr__ implementation used %r to obtain the standard representation of the attributes to be printed

List Comprehensions and Generator Expressions

Listcomp for Cartesian Product

colors = ['black', 'white']
sizes = ['S', 'M', 'L']

tshirts = [(color, size) for color in colors for size in sizes]
[('black', 'S'), ('black', 'M'), ('black', 'L'), ('white', 'S'),
 ('white', 'M'), ('white', 'L')]

Generator Expression

for tshirt in ('%s %s' % (c, s) for c in colors for s in sizes):
... print(tshirt)

Tuples

lax_coordinates = (33.9425, -118.408056) 

Tuple Unpacking

swapping the values of variables without using a temporary variable:

b, a = a, b

prefixing an argument with a star when calling a function:

>>> t = (20, 8)
>>> divmod(*t)
(2, 4)

os.path.split() function builds a tuple (path, last_part) from a filesystem path:

>>> import os
>>> _, filename = os.path.split('/home/luciano/.ssh/idrsa.pub')
>>> filename
'idrsa.pub'

grab excess arguments with *:

>>> a, b, *rest = range(5)
>>> a, b, rest
(0, 1, [2, 3, 4])

Named Tuples

collections.namedtuple take exactly the same amount of memory as tuples because the field names are stored in the class

Slicing

  • s[start:stop:stride]
  • can name slices for readability
>>> l = list(range(10))
>>> l
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> l[2:5] = [20, 30]
>>> l
[0, 1, 20, 30, 5, 6, 7, 8, 9]

Using + and * with Sequences

>>> l = [1, 2, 3]
>>> l * 5
[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]
  • Be careful with using * to create a list of mutable items, as it will result in multiple references to the same mutable item
  • Repeated concatenation of immutable sequences is inefficient, because instead of just appending new items, the interpreter has to copy the whole target sequence to create a new one with the new items concatenated

Dictionaries and Sets

Dictcomp

country_code = {country: code for code, country in DIAL_CODES}

>>> country_code
{'China': 86, 'India': 91, 'Bangladesh': 880, 'United States': 1,
'Pakistan': 92, 'Japan': 81, 'Russia': 7, 'Brazil': 55, 'Nigeria':
234, 'Indonesia': 62}

Handle Missing Keys with setdefault

Bad way

if key not in my_dict: # 3 lines, 2 index lookups (3 if not found)
    my_dict[key] = []
my_dict[key].append(new_value)

Better way (1)

my_dict.setdefault(key, []).append(new_value) # one line, single index lookup

Better way (2)

instantiate a defaultdict with a callable which used to produce a default value whenever __getitem__ is passed a non-existent key

index = collections.defaultdict(list)

Better way (3)

  • subclass collections.UserDict and provide a __missing__ method, the standard dict.__getitem__ will call it whenever a key is not found, instead of raising KeyError
  • UserDict does not inherit from dict, but has an internal dict instance, called data, which holds the actual items. This avoids undesired recursion when coding special methods like __setitem__, and simplifies the coding of __contains__

StrKeyDict always converts non-string keys to str — on insertion, update and lookup

import collections
class StrKeyDict(collections.UserDict):
    def __missing__(self, key):
        if isinstance(key, str):
            raise KeyError(key)
        return self[str(key)]
    def __contains__(self, key):
        return str(key) in self.data
    def __setitem__(self, key, item):
        self.data[str(key)] = item 
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment