stupid awesome python tricks

27
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

Upload: bryan-helmig

Post on 14-Apr-2017

118 views

Category:

Engineering


5 download

TRANSCRIPT

Page 1: Stupid Awesome Python Tricks

stupidstupid awesome

stupid awesome pythonstupid awesome python tricks

stupid awesome python tricks stupidstupid awesome python tricks stupid awesome

stupid awesome python tricks stupid awesome pythonstupid awesome python tricks stupid awesome python tricks

stupid awesome python tricks stupid awesome python bryan helmig

Page 2: Stupid Awesome Python Tricks

# 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

Page 3: Stupid Awesome Python Tricks

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'

Page 4: Stupid Awesome Python Tricks

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'

Page 5: Stupid Awesome Python Tricks

# 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!

Page 6: Stupid Awesome Python Tricks

# 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)

Page 7: Stupid Awesome Python Tricks

# 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!

Page 8: Stupid Awesome Python Tricks

# 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')#

Page 9: Stupid Awesome Python Tricks

# 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

Page 10: Stupid Awesome Python Tricks

# 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!

Page 11: Stupid Awesome Python Tricks

# 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)

Page 12: Stupid Awesome Python Tricks

# 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 []?')

Page 13: Stupid Awesome Python Tricks

# activate slice abuse

@parens_be_gonedef sum_nums(*args): return sum(args)

@parens_be_gonedef shout_it(name): return 'HELLO {}!'.format(name.upper())

print sum_nums[1, 2, 3, 4, 5]# 15

print shout_it['bryan']# HELLO WORLD!

Page 14: Stupid Awesome Python Tricks

# 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%

Page 15: Stupid Awesome Python Tricks

# 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

Page 16: Stupid Awesome Python Tricks

# 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

Page 17: Stupid Awesome Python Tricks

# 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))

Page 18: Stupid Awesome Python Tricks

# magic methods to act like you aren't using python continued

@pipedef _map(iterable, func): return [func(i) for i in iterable]

@pipedef _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']

Page 19: Stupid Awesome Python Tricks

# 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)

Page 20: Stupid Awesome Python Tricks

# 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

Page 21: Stupid Awesome Python Tricks

# 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)

Page 22: Stupid Awesome Python Tricks

# 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

Page 23: Stupid Awesome Python Tricks

# 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 = specialintprint 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/

Page 24: Stupid Awesome Python Tricks

# 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', ''], ...]

Page 25: Stupid Awesome Python Tricks

# 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

Page 26: Stupid Awesome Python Tricks

# 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]]

Page 27: Stupid Awesome Python Tricks

# mini lisp calc continued

import operatorglobal_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