Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Stupid Awesome Python Tricks

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest. →

Stupid Awesome Python Tricks

Highlights a bunch of different Python tricks and tips - from the stupid to the awesome (and a bit of both).

See how to register a 'str'.decode('hail_mary') codec, call_functions[1, 2, 3] instead of call_functions(1, 2, 3), creating a "Clojure-like" threading syntax by overloading the pipe operator, create useful equality mocks by overloading the equality operator, ditch JSON for pySON and put together a tiny lisp based on Norvig's awesome article.

Avatar for Bryan Helmig

Bryan Helmig

June 03, 2016

Other Decks in Programming

Transcript

  1. stupid stupid awesome stupid awesome python stupid awesome python tricks

    stupid awesome python tricks stupid stupid awesome python tricks stupid awesome stupid awesome python tricks stupid awesome python stupid awesome python tricks stupid awesome python tricks stupid awesome python tricks stupid awesome python bryan helmig
  2. # python has... oddities import sys print >>sys.stdout, 'hello world!'

    # hello world! from __builtin__ import int # ... uh thanks? from __builtin__ import True as False # uh oh... from __future__ import braces # ... SyntaxError: not a chance
  3. likes_art = True # trivia: what am i? where_to_go =

    likes_art and 'museum' or 'nascar' # this is another way to do it... where_to_go = ('nascar', 'museum')[bool(likes_art)] # lazily now! where_to_go = (lambda: 'nascar', lambda: 'museum')[bool(likes_art)]() # greanevrf - gunax tbq sbe clguba >2.5! # jure_g_tb = 'zhfrhz' vs yvxrf_neg ryfr 'anfpne'
  4. likes_art = True # trivia: what am i? where_to_go =

    likes_art and 'museum' or 'nascar' # this is another way to do it... where_to_go = ('nascar', 'museum')[bool(likes_art)] # lazily now! where_to_go = (lambda: 'nascar', lambda: 'museum')[bool(likes_art)]() # ternaries - thank god for python >2.5! where_to_go = 'museum' if likes_art else 'nascar'
  5. # str encoding print 'hello world!'.encode('rot13') # uryyb jbeyq! print

    'hello world!'.encode('rot13').decode('rot13') # hello world! print 'hello world!.'.encode('zlib') # x\x9c\xcbH\xcd\xc9\xc9W(\xcf/\xcaIQT(\xc9\xc8... print 'hello world!'.encode('zlib').decode('zlib') # hello world!
  6. # registering custom str encoding def register_codec(name, encode=None, decode=None): import

    codecs if not encode: def encode(val): raise Exception(name + ' does not support encoding') if not decode: def decode(val): raise Exception(name + ' does not support decoding') def codec(searched_name): if searched_name != name: return None return codecs.CodecInfo( name=name, encode=encode, decode=decode) codecs.register(codec)
  7. # custom "reverser" encoding def reverser(val): return val[::-1], len(val) register_codec('reverser',

    encode=reverser, decode=reverser) print 'hello world!'.encode('reverser') # !dlrow olleh print 'hello world!'.encode('reverser').decode('reverser') # hello world!
  8. # custom "hail mary" decoder def hail_mary_decode(val): import chardet result

    = chardet.detect(val) return val.decode(result['encoding']), len(val) register_codec('hail_mary', decode=hail_mary_decode) print '\xe3\x82\xab\xe3\x83\xac\xe3\x83\xb3\xe3\x83\x80'.decode('utf-8') # θϹЀύ print '\xe3\x82\xab\xe3\x83\xac\xe3\x83\xb3\xe3\x83\x80'.decode('hail_mary') # θϹЀύ print '\xa5\xab\xa5\xec\xa5\xf3\xa5\xc0'.decode('euc_jp') # θϹЀύ print '\xa5\xab\xa5\xec\xa5\xf3\xa5\xc0'.decode('hail_mary') # θϹЀύ
  9. # slicing / indexing / keying print range(10)[0] # 0

    print range(10)[0:5] # [0, 1, 2, 3, 4] print range(10)[::2] # [0, 2, 4, 6, 8] print range(10)[::-1] # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] print dict(hello='world')['hello'] # world
  10. # deep slicing / indexing / keying class PassThrough(object): def

    __getitem__(self, name): # magic method to return syntax args directly return name pass_through = PassThrough() print pass_through[1] # 1 print pass_through[1, 2, 3] # (1, 2, 3) print pass_through['hello world!'] # hello world!
  11. # slicing gets weird print pass_through[:] # slice(None, None, None)

    print pass_through[...] # Ellipsis print pass_through[..., ..., ..., ...] # (Ellipsis, Ellipsis, Ellipsis, Ellipsis) print pass_through[..., :, ...] # (Ellipsis, slice(None, None, None), Ellipsis)
  12. # abusing slicing class parens_be_gone(object): # square brackets are better

    than parens def __init__(self, func): self.func = func def __getitem__(self, args): if not isinstance(args, tuple): args = (args,) return self.func(*args) def __call__(self, *a, **kw): raise Exception('We do not like (), try []?')
  13. # activate slice abuse @parens_be_gone def sum_nums(*args): return sum(args) @parens_be_gone

    def shout_it(name): return 'HELLO {}!'.format(name.upper()) print sum_nums[1, 2, 3, 4, 5] # 15 print shout_it['bryan'] # HELLO WORLD!
  14. # magic methods class Roughly(object): def __init__(self, num): self.num =

    num def __invert__(self): return '%d ± 10%' % self.num print ~Roughly(5) # 5 ± 10%
  15. # magic methods continued class DoublePlus(object): def __init__(self, plusses=0): self.plusses

    = plusses def __pos__(self): return DoublePlus(self.plusses + 1) def __repr__(self): return '%d plusses' % self.plusses print ++DoublePlus() # 2 plusses
  16. # complementary magic methods for operators class Man(object): def __add__(self,

    right_other): if right_other == 'Dog': return 'best buds' return self def __radd__(self, left_other): if left_other == 'Dog': return 'bestest buds' return self print Man() + 'Dog' # best buds print 'Dog' + Man() # bestest buds
  17. # magic methods to act like you aren't using python

    class pipe(object): def __init__(self, func): self.func = func def __ror__(self, other): return self.func(other) def __call__(self, *a, **kw): return pipe(lambda x: self.func(x, *a, **kw))
  18. # magic methods to act like you aren't using python

    continued @pipe def _map(iterable, func): return [func(i) for i in iterable] @pipe def _filter(iterable, func): return [i for i in iterable if func(i)] print (range(10) | _map(lambda i: i * 3) | _filter(lambda i: i % 2 == 0) | _map(str)) # ['0', '6', '12', '18', '24']
  19. # magic methods for elegant DSLs class RegexEquals(object): def __init__(self,

    regex): import re self.regex = re.compile(regex) def __eq__(self, other): return bool(self.regex.match(other)) class TypeEquals(object): def __init__(self, *tipes): self.tipes = tipes def __eq__(self, other): return isinstance(other, self.tipes)
  20. # magic methods for elegant DSLs continued URL = RegexEquals(r'(https?|ftp)://[^\s/$.?#].[^\s]*')

    NUM = RegexEquals(r'\d+') INT = TypeEquals(int, long) print 'larry' == URL # False print 'http://zapier.com/' == URL # True print '123' == NUM # True print {'url': 'http://zapier.com/', 'visits': 4} == {'url': URL, 'visits': INT} # True
  21. # magic methods for abuse class DidYouMean(object): def __getattr__(self, name):

    if name.startswith('__'): raise AttributeError('No magic did you mean.') from difflib import SequenceMatcher scored_attrs = [ (SequenceMatcher(None, name, attr).ratio(), attr) for attr in dir(self) if not attr.startswith('__') ] sorted_attrs = sorted([ (score, attr) for score, attr in scored_attrs if score > 0.5 ], reverse=True) best_name = next(iter(sorted_attrs), (None, None))[1] if not best_name: raise AttributeError('No "matching" `%s` attribute' % name) return getattr(self, best_name)
  22. # activate magic methods abuse class SomeModel(DidYouMean): first_name = 'bryan'

    last_name = 'helmig' person = SomeModel() print person.first_name # bryan print person.furst_name # bryan print person.address # .. AttributeError: No "matching" `address` attribute
  23. # magic methods for evil class specialint(int): def __add__(self, right_other):

    if self == 1 and right_other == 1: return 3 return super(specialint, self).__add__(right_other) print specialint(1) + 1 # 3 __builtins__.int = specialint print int(1) + 1 # 3 print 1 + 1 # 2 # literals get coerced way before builtins are referenced... ¯\_(ϑ)_/¯ # https://benkurtovic.com/2015/01/28/python-object-replacement.html # http://blaag.haard.se/Using-the-AST-to-hack-constants-into-Python/
  24. # ast / parser import parser tree = parser.expr('(a +

    1) or {1: "test"}').tolist() print tree # [258, [327, [312, [313, [314, [315, [316, [317, [318, [7, '('] .. # [ ... [3, '"test"'], [27, '}']], [4, ''], ...] def recurse(val): import token if isinstance(val, int): return token.tok_name.get(val, val) elif isinstance(val, list): return [recurse(v) for v in val] return val print recurse(tree) # [258, [327, [312, [313, [314, [315, [316, [317, [318, [7, '('] .. # [ ... ['STRING', '"test"'], ['RBRACE', '}']], ['NEWLINE', ''], ...]
  25. # ast tools can be repurposed and practical def pyson(val):

    # JSON is lame. Eval is lame. import ast return ast.literal_eval(val) print pyson('"hello world!"') # hello world! print pyson("{1234: 'integer keys!'}") # {1234: 'integer keys!''} print pyson('import json') # ... SyntaxError: invalid syntax
  26. # mini lisp calc def tokenize(program): return program.replace('(', ' (

    ').replace(')', ' ) ').split() def reshape_tokens(tokens): token = tokens.pop(0) if '(' == token: deep_tokens = [] while tokens[0] != ')': deep_tokens.append(reshape_tokens(tokens)) tokens.pop(0) return deep_tokens try: return int(token) except: return token program = '(* 2 (+ 3 5))' print tokenize(program) # ['(', '*', '2', '(', '+', '3', '5', ')', ')'] print reshape_tokens(tokenize(program)) # ['*', 2, ['+', 3, 5]]
  27. # mini lisp calc continued import operator global_env = {'+':

    operator.add, '-': operator.sub, '*': operator.mul, '/': operator.div} def evaluate(tokens, env=global_env.copy()): if isinstance(tokens, basestring) and tokens in env: return env[tokens] elif not isinstance(tokens, list): return tokens else: func = evaluate(tokens[0], env) args = [evaluate(arg, env) for arg in tokens[1:]] return func(*args) program = '(* 2 (+ 3 5))' print evaluate(read_tokens(tokenize(program))) # 16 # http://norvig.com/lispy.html & http://norvig.com/lispy2.html