A Few of My Favorite (Python) Things
Upcoming SlideShare
Loading in...5
×
 

A Few of My Favorite (Python) Things

on

  • 15,499 views

Python's "batteries included" philosophy means that it comes with an astonishing amount of great stuff. On top of that, there's a vibrant world of third-party libraries that help make Python even more ...

Python's "batteries included" philosophy means that it comes with an astonishing amount of great stuff. On top of that, there's a vibrant world of third-party libraries that help make Python even more wonderful. We'll go on a breezy, example-filled tour through some of my favorites, from treasures in the standard library to great third-party packages that I don't think I could live without, and we'll touch on some of the fuzzier aspects of the Python culture that make it such a joy to be part of.

Statistics

Views

Total Views
15,499
Views on SlideShare
13,528
Embed Views
1,971

Actions

Likes
38
Downloads
358
Comments
1

16 Embeds 1,971

http://mike.pirnat.com 1651
http://www.scoop.it 186
http://lanyrd.com 78
http://localhost 11
http://coderwall.com 10
http://whitesmell.feezen.com 9
http://us-w1.rockmelt.com 9
https://twitter.com 4
http://feeds.feedburner.com 3
http://a0.twimg.com 3
http://webcache.googleusercontent.com 2
https://duckduckgo.com 1
http://twitter.com 1
http://translate.googleusercontent.com 1
http://paper.li 1
https://www.rebelmouse.com 1
More...

Accessibility

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

A Few of My Favorite (Python) Things A Few of My Favorite (Python) Things Presentation Transcript

  • A Few of My Favorite Things Mike Pirnat • AG Interactive • CodeMash 2012
  • A Few of My Favorite Things Mike Pirnat • AG Interactive • CodeMash 2012
  • Disclaimers
  • The Language
  • _ ( = 255, lambda V ,B,c :c and Y(V*V+B,B, c -1)if(abs(V)<6)else ( 2+c-4*abs(V)**-0.4)/i ) ;v, x=1500,1000;C=range(v*x );import struct;P=struct.pack;M, j =<QIIHHHH,open(M.bmp,wb).writefor X in j(BM+P(M,v*x*3+26,26,12,v,x,1,24))or C: i ,Y=_;j(P(BBB,*(lambda T:(T*80+T**9 *i-950*T **99,T*70-880*T**18+701* T **9 ,T*i**(1-T**45*2)))(sum( [ Y(0,(A%3/3.+X%v+(X/v+ A/3/3.-x/2)/1j)*2.5 /x -2.7,i)**2 for A in C [:9]]) /9) ) ) http://preshing.com/20110926/high-resolution-mandelbrot-in-obfuscated-python
  • http://preshing.com/20110926/high-resolution-mandelbrot-in-obfuscated-python
  • The Interactive Shell$ pythonPython 2.7.1 (r271:86832, Jun 16 2011, 16:59:05)[GCC 4.2.1 (Based on Apple Inc. build 5658) (LLVM build2335.15.00)] on darwinType "help", "copyright", "credits" or "license" formore information.>>>
  • Docstrings...class Frobulator(object): """It frobulates things.""" def __init__(self, doohickey): """A Frobulator needs a doohickey.""" self.doohickey = doohickey def frobulate(self): """Frobulate ALL the things!""" print "Frobulating..." doodad = self.doohickey() return FrobulatedThing(doodad)class FrobulatedThing(object): """A thing which has been frobulated.""" def __init__(self, thing): """Make a thing into a frobulated thing.""" self.thing = thing
  • ...and Help>>> help(Frobulator)Help on class Frobulator in module frobulator:class Frobulator(__builtin__.object) | It frobulates things. | | Methods defined here: | | __init__(self, doohickey) | A Frobulator needs a doohickey. | | frobulate(self) | Frobulate ALL the things! | ...
  • Comprehensions• List comprehensions• Set comprehensions• Dictionary comprehensions• Generator expressions
  • List Comprehensionsx = [item for item in series]x = [do_something(item) for item in series if expression]things = [Thingy.from_data(x) for x in database_results]partiers = [x for x in codemashers if x.slides_done()]
  • List Comprehensionsbooze = [beer, wine, scotch, gin]soft_drinks = [water, soda, juice]a = [(x, y) for x in booze for y in soft_drinks][(beer, water), (beer, soda), (beer, juice),(wine, water), (wine, soda), (wine, juice),(scotch, water), (scotch, soda), (scotch,juice), (gin, water), (gin, soda), (gin,juice)]
  • List Comprehensionsb = [x for x in zip(booze, soft_drinks)][(beer, water), (wine, soda), (scotch, juice)]
  • Set Comprehensionss = {v for v in CODEMASH ROCKS if v not in ABCD}set([ , E, H, K, M, O, S, R])
  • Dictionary Comprehensions d = {key: value for key, value in list_of_tuples} d = { Mike: Python, Jim: Ruby, Brad: UX, ... } d1 = {val: key for key, val in d.items()} {Python: Mike, Ruby: Jim, UX: Brad, ...}
  • Generatorsdef f(how_many): for number in range(how_many): if number**2 > 3: yield number * 2for number in f(5): print number468
  • Generator Expressionsgen = (2*x for x in range(5) if x**2 > 3)for number in gen: print number468
  • Propertiesclass Foo(object): def __init__(self, bar=42): self.set_bar(bar) def get_bar(self): return self.bar def set_bar(self, bar): self.bar = int(bar)
  • Propertiesclass Foo(object): def __init__(self, bar=42): self.bar = bar @property def bar(self): return self._bar @bar.setter def bar(self, x): self._bar = int(x)
  • Propertiesclass Foo(object): def __init__(self, bar=42): self.bar = bar @property def bar(self): return self._bar @bar.setter def bar(self, x): self._bar = int(x)
  • Properties>>> foo = Foo()>>> foo.bar42>>> foo.bar = abcTraceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 9, in barValueError: invalid literal for int() with base 10:abc
  • Decorators• That funny-looking @ thing• Similar to Java’s annotations• Replaces a function with a wrapper• Augment functionality in a reusable way
  • Decoratorsdef be_excellent(wrapped): def wrapper(*args, **kwargs): print "Be excellent to each other..." return wrapped(*args, **kwargs) return wrapper@be_excellentdef party_on(who): print "...and party on, {0}!".format(who)>>> party_on(dudes)Be excellent to each other......and party on, dudes!
  • Context Managers• That funny “with” thing• Simplify calling code that needs setup and teardown• Alternative to try/finally structures• Acquire locks, open and close files, do database transactions, etc.
  • Before Context Managersfrobulator = Frobulator(...)try: frobulator.frobulate()finally: frobulator.cleanup()
  • After Context Managerswith Frobulator(...) as frobulator: frobulator.frobulate()
  • class Frobulator(object): def __init__(self, ...): ... def __enter__(self): print "Preparing to frobulate..." return self def __exit__(self, exc_type, exc_value, exc_tb): print "Frobulation complete!" def frobulate(self): print "Frobulating!" ...
  • with Frobulator() as frobulator: frobulator.frobulate()Preparing to frobulate...Frobulating!Frobulation complete!
  • import contextlib@contextlib.contextmanagerdef make_frobulator(): print "Preparing to frobulate..." yield Frobulator() print "Frobulation complete!"with make_frobulator() as frobulator: frobulator.frobulate()Preparing to frobulate...Frobulating!Frobulation complete!
  • The Standard Library
  • Itertools• Tools for iterating on sequences• Inspired by functional languages• Fast and memory efficient• Combine to express more complicated algorithms
  • Itertools: chainfrom itertools import chainfor x in chain([1, 2, 3], [42, 1138, 2112]): print x,1 2 3 42 1138 2112
  • Itertools: izipfrom itertools import izipfor x in izip([1, 2, 3], [42, 1138, 2112]): print x,(1, 42) (2, 1138) (3, 2112)
  • Itertools: islicefrom itertools import islice, countfor x in islice(count(), 5): print x,for x in islice(count(), 5, 10): print x,for x in islice(count(), 0, 100, 10): print x,0 1 2 3 45 6 7 8 90 10 20 30 40 50 60 70 80 90
  • Itertools: islicefrom itertools import islice, countfor x in islice(count(), 5): print x,for x in islice(count(), 5, 10): print x,for x in islice(count(), 0, 100, 10): print x,0 1 2 3 45 6 7 8 90 10 20 30 40 50 60 70 80 90
  • Itertools: imapfrom itertools import imapfor thingy in imap(Thingy.from_data, database_results): ...
  • Itertools: cyclefrom itertools import cyclefor x in cycle([wake up, meet Ned, romance Rita]): print xwake upmeet Nedromance Ritawake upmeet Nedromance Ritawake upmeet Ned...
  • Itertools: repeatfrom itertools import repeatfor x in repeat("stop hitting yourself", 5): print xstop hitting yourselfstop hitting yourselfstop hitting yourselfstop hitting yourselfstop hitting yourself
  • Functools• Tools for manipulating functions• Partial• Wraps a callable with default arguments• Alternative to lambdas and closures
  • Functools: Partialfrom functools import partialdef f(a, b=2): print a, bf1 = partial(f, fixed_a)f2 = partial(f, b=fixed_b)>>> f1(b=1138)fixed_a 1138>>> f2(1138)1138 fixed_b
  • Functools: Partialdef category_is(category, item): categories = [x.lower() for x in item.categories] if category.lower() in categories: return True return Falseis_python = partial(category_is, python)is_cat_pictures = partial(category_is, catpix)...python_posts = [item for item in blog_posts if is_python(item)]cat_posts = [item for item in blog_posts if is_cat_pictures(item)]
  • Collections• Beyond the basic list, dict, tuple, and set...• Counter• Defaultdict• OrderedDict• Namedtuple• ...and more
  • Countercounter = {}for char in "Hello there, CodeMash!": if char not in counter: counter[char] = 1 else: counter[char] += 1print counter{a: 1, : 2, C: 1, e: 4, d: 1, H: 1, M: 1,l: 2, o: 2, ,: 1, s: 1, r: 1, !: 1, t: 1,h: 2}
  • Countercounter = {}for char in "Hello there, CodeMash!": if char not in counter: counter[char] = 1 else: counter[char] += 1print counter{a: 1, : 2, C: 1, e: 4, d: 1, H: 1, M: 1,l: 2, o: 2, ,: 1, s: 1, r: 1, !: 1, t: 1,h: 2}
  • Counterfrom collections import Countercounter = Counter("Hello there, CodeMash!")print counterCounter({e: 4, : 2, l: 2, o: 2, h: 2, a: 1,C: 1, d: 1, H: 1, M: 1, ,: 1, s: 1, r: 1,!: 1, t: 1})
  • Counterfrom collections import Countercounter = Counter("Hello there, CodeMash!")print counterCounter({e: 4, : 2, l: 2, o: 2, h: 2, a: 1,C: 1, d: 1, H: 1, M: 1, ,: 1, s: 1, r: 1,!: 1, t: 1})
  • Namedtupleimport mathdef distance(a, b): return math.sqrt( (a[0] - b[0])**2 + (a[1] - b[1])**2 + (a[2] - b[2])**2 )a = (1, 2, 3)b = (-1, -2, 42)print distance(a, b)39.25557285278104
  • Namedtuplefrom collections import namedtuplePoint = namedtuple(Point, x y z)def distance(a, b): return math.sqrt( (a.x - b.x)**2 + (a.y - b.y)**2 + (a.z - b.z)**2 )a = Point(x=1, y=2, z=3)b = Point(-1, -2, 42)print distance(a, b)39.25557285278104
  • Difflibs1 = """Lorem ipsum dolor sit amet, consectetur adipiscing elit.Phasellus dui nunc, faucibus id ullamcorper eget, tempusvitae nisl. Donec quis semper risus. Curabitur sit amettellus eget metus accumsan porta nec nec lorem. Ut vitaesem nisl. Praesent pulvinar feugiat nibh fringillasemper. Nullam cursus tempor lorem ut egestas. Nullamsuscipit gravida turpis ac porttitor. Curabitur eleifendaugue at risus commodo pretium. Aliquam eget magnarisus, ut lobortis metus. Cum sociis natoque penatibuset magnis dis parturient montes, nascetur ridiculus mus.Etiam non magna sit amet nulla porttitor molestie sitamet vel sem. Vestibulum sit amet nisl a velitadipiscing porta id non urna. Duis ullamcorper dictumipsum sit amet congue."""
  • Difflibs2 = """Lorem ipsum dolor sit amet, consectetur adipiscing elit.Phasellus dui nunc, faucibus id ullamcorper eget, tempusvitae nisl. Donec quis semper risus. Curabitur sit amettellus eget metus accumsan porta nec nec lorem. Ut vitaesem nisl. Praesent pulvinar feugiat nibh fringillasemper. Nullam cursus tempor lorem ut egestas. Nullamsuscipit gravida turpis ac porttitor. Curabitur eleifendaugue at risus commodo pretium. Aliquam eget magnarisus, ut lobortis montes. Cum sociis natoque penatibuset magnis dis parturient metus, nascetur ridiculus mus.Etiam non magna sit amet nulla porttitor molestie sitamet vel sem. Vestibulum sit amet nisl a velitadipiscing porta id non urna. Duis ullamcorper dictumipsum sit amet congue."""
  • Difflibimport difflibdiffer = difflib.Differ()diff = differ.compare(s1.splitlines(), s2.splitlines())print n.join(diff)
  • Difflib Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus dui nunc, faucibus id ullamcorper eget, tempus vitae nisl. Donec quis semper risus. Curabitur sit amet tellus eget metus accumsan porta nec nec lorem. Ut vitae sem nisl. Praesent pulvinar feugiat nibh fringilla semper. Nullam cursus tempor lorem ut egestas. Nullam suscipit gravida turpis ac porttitor. Curabitur eleifend augue at risus commodo pretium. Aliquam eget magna- risus, ut lobortis metus. Cum sociis natoque penatibus? --+ risus, ut lobortis montes. Cum sociis natoque penatibus? +++- et magnis dis parturient montes, nascetur ridiculus mus.? ^^ ^+ et magnis dis parturient metus, nascetur ridiculus mus.? ^ ^ Etiam non magna sit amet nulla porttitor molestie sit amet vel sem. Vestibulum sit amet nisl a velit adipiscing porta id non urna. Duis ullamcorper dictum ipsum sit amet congue.
  • Difflibdiff = difflib.unified_diff( s1.splitlines(), s2.splitlines(), lineterm=)print n.join(diff)
  • Difflib---+++@@ -7,8 +7,8 @@ semper. Nullam cursus tempor lorem ut egestas. Nullam suscipit gravida turpis ac porttitor. Curabitur eleifend augue at risus commodo pretium. Aliquam eget magna-risus, ut lobortis metus. Cum sociis natoque penatibus-et magnis dis parturient montes, nascetur ridiculus mus.+risus, ut lobortis montes. Cum sociis natoque penatibus+et magnis dis parturient metus, nascetur ridiculus mus. Etiam non magna sit amet nulla porttitor molestie sit amet vel sem. Vestibulum sit amet nisl a velit adipiscing porta id non urna. Duis ullamcorper dictum
  • Ast• Use Python to process abstract syntax trees of Python grammar• Helps introspect about what the current grammar looks like• Helps write secure code
  • Ast# Danger:foo = eval(bar)# Safe:foo = ast.literal_eval(bar)
  • Multiprocessing• Like threading, but with subprocesses• Local and remote concurrency• Spawn processes or entire pools• Communicate via queues or pipes• Shared state via shared memory or manager/server process
  • import osfrom multiprocessing import Processdef info(title): print title print parent process:, os.getppid() print process id:, os.getpid()def f(name): info(function f) print hello, nameif __name__ == __main__: info(main) p = Process(target=f, args=(world,)) p.start() p.join()
  • $ python process.pymainparent process: 18647process id: 31317----------function fparent process: 31317process id: 31318----------hello world
  • import osfrom multiprocessing import Pooldef g(x): info(function g(+str(x)+)) return x * xif __name__ == __main__: info(main) pool = Pool(2) print pool.map(g, range(100))
  • $ python process.py parent process: 31369main parent process: 31369module name: __main__ process id: 31370parent process: 18647 process id: 31371process id: 31369 -------------------- ----------function g(0) function g(4)module name: __main__ module name: __main__parent process: 31369 parent process: 31369process id: 31370 function g(14)---------- process id: 31370function g(1) module name: __main__module name: __main__ ----------parent process: 31369 parent process: 31369process id: 31370 function g(5)---------- process id: 31371function g(13) module name: __main__function g(2)module name: __main__ ...parent process: 31369process id: 31370 [0, 1, 4, 9, 16, 25, ..., 8836,---------- 9025, 9216, 9409, 9604, 9801]module name: __main__function g(3)module name: __main__
  • Third Party Packages
  • Third Party Packages• Most available on PyPI--http://pypi.python.org• pip install packagename• easy_install packagename
  • Virtualenv• Makes an isolated Python environment• Don’t pollute your global site-packages• Insulates Python projects from one another• Dont have to be root
  • Virtualenv$ virtualenv directory$ virtualenv --python=/path/to/specific/python directory$ cd directory$ . bin/activate$ easy_install whatever$ pip install whatever...do stuff...$ deactivate
  • Datetime and Dateutil• Python’s datetime provides date, time, datetime, and timedelta objects• Dateutil provides powerful extensions• Parsing• Olson-driven timezones• Recurrence
  • Parsing>>> from dateutil.parser import parse>>> parse(01/01/2012)datetime.datetime(2012, 1, 1, 0, 0)>>> parse(2012-01-01)datetime.datetime(2012, 1, 1, 0, 0)
  • Timezones>>> from dateutil import zoneinfo>>> zone = zoneinfo.gettz(US/Eastern)>>> zonetzfile(America/New_York)>>> zone2 = zoneinfo.gettz(US/Hawaii)>>> dt = datetime.now(zone)>>> dtdatetime.datetime(2012, 1, 13, 13, 46, 54, 997825,tzinfo=tzfile(America/New_York))>>> dt.astimezone(zone2)datetime.datetime(2012, 1, 13, 8, 46, 54, 997825,tzinfo=tzfile(Pacific/Honolulu))
  • Recurrence>>> from dateutil.rrule import rrule, YEARLY, MONTHLY,WEEKLY, DAILY, HOURLY, MINUTELY, SECONDLY, BY_EASTER>>> rr = rrule(YEARLY, dtstart=datetime(1948, 2, 24))>>> rr.after(datetime(2012, 1, 1))datetime(2012, 2, 24, 0, 0)>>> rr.between(datetime(2010, 1, 1), datetime(2012, 1, 1))[datetime(2010, 2, 24, 0, 0), datetime(2011, 2, 24, 0, 0)]
  • howoldismykid.comdef periods_between(freq, start_date, end_date):    # dateutil.rrule falls down for monthly recurrences where the start # dates day is greater than the number of days in a subsequent month # in the range; ie, when start date is 10/31/2010, and end date is # 3/13/2011, rrules between will only produce 2 instances, 12/31 and # 1/31, rather than 4.    if freq == MONTHLY and start_date.day > 28:        start_date = datetime.datetime(start_date.year, start_date.month,                28, start_date.hour, start_date.minute, start_date.second)    # Same problem but for "Pirates of Penzance" leap day birthdays...    elif freq == YEARLY and is_leap_day(start_date):        start_date = datetime.datetime(start_date.year, start_date.month,                28, start_date.hour, start_date.minute, start_date.second)    rr = rrule(freq, dtstart=start_date)    periods = len(rr.between(start_date, end_date))    return periods
  • howoldismykid.com>>> start_date = datetime(2007, 9, 10)>>> end_date = datetime(2012, 1, 13)>>> periods_between(YEARLY, start_date, end_date)4>>> periods_between(MONTHLY, start_date, end_date)52>>> periods_between(WEEKLY, start_date, end_date)226>>> periods_between(DAILY, start_date, end_date)1585
  • Nose• Find, run, and report results of test suites• Low-friction setup• Extendable with plugins (more later)• http://readthedocs.org/docs/nose/en/latest/
  • Nose# test_foo.pydef test_a_thing(): assert Falsedef test_another_thing(): assert True
  • Nose$ nosetests tests/test_foo.pyF.======================================================================FAIL: test_foo.test_a_thing----------------------------------------------------------------------Traceback (most recent call last): File "/Users/mpirnat/Code/frobulator/lib/python2.7/site-packages/nose-1.1.2-py2.7.egg/nose/case.py", line 197, in runTest self.test(*self.arg) File "/Users/mpirnat/Code/frobulator/src/frobulator/tests/test_foo.py",line 2, in test_a_thing assert FalseAssertionError----------------------------------------------------------------------Ran 2 tests in 0.001sFAILED (failures=1)
  • Noseclass TestWhenFrobulating(object): def setup(self): self.frobulator = Frobulator() self.frobulator.frobulate() def test_that_it_frobulated(self): assert self.frobulator.has_frobulated def teardown(self): self.frobulator.cleanup()
  • Nose$ nosetests.--------------------------------------------------------Ran 1 test in 0.005sOK
  • Nose: Useful Flags• --processes=number• -x• --pdb• --pdb-failures• --failed
  • Nose Plugins• Add custom behavior to your test run• Get coverage statistics (coverage)• Measure cleanliness with PEP-8 (tissue)• Emit human-readable spec-style results (pinocchio)• Or write your own...
  • class TestGeneratesLotsOfFailures(object): def test_generates_failures(self): def _make_a_test(i): # start with some wins if i < 7: assert True # but then it hits the fan... elif i < 30: assert False # then be a little random elif i % 3 == 0: assert False else: assert True for i in range(50): yield _make_a_test, i
  • $ nosetests test_epic_fail.py.......FFFFFFFFFFFFFFFFFFFFFFFF..F..F..F..F..F..F.[lots of test failure output; use your imagination...]--------------------------------------------------------Ran 50 tests in 0.010sFAILED (errors=30)
  • import osfrom nose.plugins import Pluginclass F7U12(Plugin): name = f7u12 enabled = True def options(self, parser, env=os.environ): super(F7U12, self).options(parser, env=env) def configure(self, options, config): super(F7U12, self).configure(options, config) self.config = config if not self.enabled: return
  • def setOutputStream(self, stream): self.stream = stream return self.streamdef begin(self): self.failure_count = 0def handleFailure(self, test, err): self.failure_count += 1 if self.failure_count < 8: self.stream.write(F) else: self.stream.write(U) return True
  • $ nosetests --with-f7u12 test_epic_fail.py.......FFFFFFFFUUUUUUUUUUUUUUUU..U..U..U..U..U..U.[lots of test failure output; use your imagination...]--------------------------------------------------------Ran 50 tests in 0.010sFAILED (errors=30)
  • Mock• Mock object framework• Avoid writing custom stubs• Uses “Action/Assert” model (not “Record/ Replay”)• Can also patch out module and class attributes in the scope of a test• http://www.voidspace.org.uk/python/mock/
  • Mockfrom mock import Mockfoo = Mock()foo.bar()foo.bar.baz(42)foo.bar.baz.return_value = "whatever"assert foo.bar.calledfoo.bar.baz.assert_called_with(42)
  • Mockclass TestWhenFrobulating(object): def setup(self): self.doohickey = Mock() self.frobulator = Frobulator(self.doohickey) self.frobulator.frobulate() def test_it_uses_the_doohickey(self): assert self.doohickey.called
  • Mockfrom mock import patchclass TestWhenFrobulating(object): @patch(frobulator.DingleHopper) def setup(self, dingle_hopper_class): self.dingle_hopper = Mock() dingle_hopper_class.return_value = self.dingle_hopper self.frobulator = Frobulator() self.frobulator.frobulate() def test_it_uses_a_dingle_hopper(self): assert self.dingle_hopper.called
  • Coverage• Measure coverage of your codebase during program execution• Integrates with several test runners to measure test coverage• http://nedbatchelder.com/code/coverage/
  • #!/usr/bin/env pythonclass Frobulator(object): """It frobulates things.""" def __init__(self, doohickey): """A Frobulator needs a doohickey.""" self.doohickey = doohickey def frobulate(self): """Frobulate ALL the things!""" print "Frobulating..." doodad = self.doohickey() return FrobulatedThing(doodad)class FrobulatedThing(object): """A thing which has been frobulated.""" def __init__(self, thing): """Make a thing into a frobulated thing.""" self.thing = thingif __name__ == __main__: x = FrobulatedThing(42)
  • #!/usr/bin/env pythonclass Frobulator(object): """It frobulates things.""" def __init__(self, doohickey): """A Frobulator needs a doohickey.""" self.doohickey = doohickey def frobulate(self): """Frobulate ALL the things!""" print "Frobulating..." doodad = self.doohickey() return FrobulatedThing(doodad)class FrobulatedThing(object): """A thing which has been frobulated.""" def __init__(self, thing): """Make a thing into a frobulated thing.""" self.thing = thingif __name__ == __main__: x = FrobulatedThing(42)
  • $ coverage run frobulator.py$ coverage report -mName Stmts Miss Cover Missing------------------------------------------frobulator 12 4 67% 8, 12-14
  • from mock import Mock, patchfrom frobulator import Frobulator, FrobulatedThingclass TestWhenFrobulating(object): def setup(self): self.doohickey = Mock() self.doohickey.return_value = 42 self.frobulator = Frobulator(self.doohickey) self.result = self.frobulator.frobulate() def test_it_uses_a_doohickey(self): assert self.doohickey.called def test_it_returns_a_frobulated_thing(self): assert isinstance(self.result, FrobulatedThing)
  • $ nosetests --with-coverage --cover-package frobulator..Name Stmts Miss Cover Missing------------------------------------------frobulator 12 1 92% 26------------------------------------------Ran 2 tests in 0.008sOK
  • Useful Flags• --cover-erase• --cover-inclusive• --cover-tests
  • Lettuce• Like Cucumber• BDD test framework• Natural language augmented by code• Big wins pairing Devs + QA + Biz• http://lettuce.it/
  • Lettuce Vocabulary• Features• Scenarios• Steps• World• Terrain
  • Feature: The website has a homepage In order to demo Lettuce As a presenter I want to test the homepage of a website Scenario: Verify the site is up Given I access the url "http://localhost:8000/" Then I get a status "200"
  • from lettuce import step, worldfrom nose.tools import assert_equalsimport requests@step(rI access the url "(.*)")def access_url(step, url): world.response = requests.get(url)@step(rI get a status "(.*)")def get_status(step, expected_status): expected_status = int(expected_status) assert_equals(world.response.status_code, expected_status)
  • Snake-Guice• Framework for Dependency Injection• Reduce coupling!• Improve testability!• http://code.google.com/p/snake-guice/
  • Snake-Guice• Bind identifiers to things you want to inject• Decorate code with injection hints• Use injector to get instance with all dependencies resolved and injected• Construct with mocks in tests, minimize the need for patching
  • Define the Identifierclass IFrobulator(object): pass
  • Decorate Your Codefrom snakeguice import injectclass WankelRotator(object): @inject(frobulator=IFrobulator) def __init__(self, frobulator): self.frobulator = frobulator
  • Decorate Your Codefrom snakeguice import injectclass WankelRotator(object): @inject(frobulator=IFrobulator) def __init__(self, frobulator): self.frobulator = frobulator
  • Configure Bindingsfrom frobulator import Frobulatorclass BindingModule(object): def configure(self, binder): binder.bind(IFrobulator, to=Frobulator)
  • Get an Instancefrom snakeguice import Injectorimport WankelRotatorinjector = Injector(BindingModule())rotator = injector.get_instance(WankelRotator)
  • Requests• A human-friendly alternative to urllib2• Maturing rapidly• http://python-requests.org/
  • Using Requestsimport requests# Authentication!r = requests.get(https://api.github.com, auth=(user, pass))print r.status_codeprint r.headers[content-type]
  • Using Requests# Posting name-value pair form datapost_data = {foo: bar, baz: quux, ...}r = requests.post(url, data=post_data)# Posting a glob of JSONr = requests.post(url, data=json.dumps(post_data))# Multipart file attachmentfiles = {my_file.xls: open(my_file.xls, rb)}r = requests.post(url, files=files)
  • Using Requests# Custom headers!headers = {X-foo: bar}r = requests.get(url, headers=headers)# Cookies!cookies = {a_cookie: is delicious}r = requests.get(url, cookies=cookies)
  • Pylons• http://docs.pylonsproject.org/en/latest/docs/ pylons.html• MVCish web framework• Glues together several component projects• Can use your preferred components• Somewhat dead (evolved into Pyramid)
  • Pylons• Request/response: webob• URL dispatch: routes• Input validation: formencode• Persistence/ORM: sqlalchemy• Session: beaker• Templates: mako
  • from routes import Mapperdef make_map(config): """Create, configure and return the routes Mapper""" map = Mapper(directory=config[pylons.paths][controllers], always_scan=config[debug]) ... map.connect(/, controller=main, action=index) map.connect(/contact, controller=contact, action=index, conditions={method:GET}) map.connect(/contact, controller=contact, action=send, conditions={method:POST}) return map
  • from howoldismykid.lib.base import BaseController, renderclass MainController(BaseController): def index(self): # Return a rendered template return render(/main.mako)
  • from formencode import Schema, validatorsclass ContactForm(Schema): email = validators.Email(not_empty=True) subject = validators.String(not_empty=True) message = validators.String(not_empty=True)
  • from pylons.decorators import validatefrom howoldismykid.lib.base import BaseController, renderfrom howoldismykid.model.forms import ContactFormclass ContactController(BaseController): def __init__(self, *args, **kwargs): BaseController.__init__(self, *args, **kwargs) self.emailer = Emailer(SMTP, USER, PW, FAKE_EMAIL) def index(self): # Return a rendered template return render(/contact.mako) @validate(schema=ContactForm(), form="index", prefix_error=False) def send(self): validated = self.form_result self.emailer.send_mail(validated[email], EMAIL, validated[subject], validated[message]) return render(/contact_done.mako)
  • <!DOCTYPE HTML><html> <head> ${self.head_tags()} ... </head> <body> <div id="header">...</div> <div id="content"> ${self.body()} </div> <div id="footer">...</div> <%include file="google_analytics.mako"/> ${self.foot_tags()} </body></html>
  • <%inherit file="/base.mako" /><%def name="head_tags()"><title>How Old Is My Kid?</title></%def><h2>Contact Us</h2><p>Suggest a new feature, let us know how were doing, or just say hi.</p><form method="POST" action="/contact" id="contact-form"> <p><label for="email">Email:</label> <input type="text" name="email"id="email" /></p> <p><label for="subject">Subject:</label> <input type="text"name="subject" id="subject" /></p> <p><label for="message">Message:</label><br /><textarea name="message" id="message"></textarea></p> <p><input type="submit" value="Submit" /></p></form><%def name="foot_tags()"><script type="text/javascript">$(document).ready(function() { $("button, input:submit").button();});</script></%def>
  • Django• https://www.djangoproject.com/• Full stack/harder to replace components• Lots of reusable apps• Admin interface• Lots of deployment options (Google)• Not dead
  • from django.conf.urls.defaults import patterns, urlurlpatterns = patterns(, url(r^/$, howoldismykid.views.main, name=main), url(r^/contact/$, howoldismykid.views.contact, name=contact),)
  • from django.shortcuts import renderdef main(request): return render(request, main.html)
  • from django import formsclass ContactForm(forms.Form): email = forms.EmailField(label=Email Address) subject = forms.CharField(label=Subject) message = forms.CharField(label=Message, widget=forms.Textarea)
  • from contact.forms import ContactFormfrom django.shortcuts import renderdef contact(request): if request.method == POST: form = ContactForm(request.POST) if form.is_valid(): emailer = Emailer(SMTP, USER, PW, FAKE_EMAIL) emailer.send_mail( form.cleaned_data[email], EMAIL, form.cleaned_data[subject], form.cleaned_data[message]) return render(request, contact_done.html) else: form = ContactForm() return render(request, contact.html, {form: form })
  • <!DOCTYPE HTML><html> <head> {% block head_tags %}...{% endblock %} ... </head> <body> <div id="header">...</header> <div id="content"> {% block content %}It goes here.{% endblock %} </div> <div id="footer">...</div> {% include "google_analytics.html" %} {% block foot_tags %}{% endblock %} </body></html>
  • {% extends base.html %}{% block head_tags %}<title>How Old Is My Kid?</title>{% endblock %}{% block content %}<h2>Contact Us</h2><p>Suggest a new feature, let us know how were doing, or just say hi.</p><form method="POST" action="/contact" id="contact-form"> {% csrf_token %} {{ form.as_p }} <p><input type="submit" value="Submit" /></p></form>{% endblock %}{% block foot_tags %}<script type="text/javascript">$(document).ready(function() { $("button, input:submit").button();});</script>{% endblock %}
  • The Culture
  • Planet Python• http://planet.python.org/• Aggregate feed of Python blogs• Great way to follow whats going on• Minimize newsgroup/mailing list burdens• Easy to be included
  • The Python Ecosystem• http://mirnazim.org/writings/python- ecosystem-introduction/• Great introduction to the Python ecosystem• Everything you need to get up and running
  • The Hitchhiker’s Guide• http://docs.python-guide.org/• Opinionated advice about using Python• Basics• Scenario-specific details and recommendations
  • PyMotW• Explored a different standard library module every week• Examples, examples, examples• http://www.doughellmann.com/PyMOTW/• The Python Standard Library by Example http://www.doughellmann.com/books/byexample/
  • Podcasts• http://www.radiofreepython.com/• http://frompythonimportpodcast.com/• http://djangodose.com/• http://advocacy.python.org/podcasts/• http://www.awaretek.com/python/• Start your own! Be the change you want...
  • PyOhio• Free as in $0.00• Columbus, Ohio• Last weekend in July• http://pyohio.org
  • PyCon• Great people• Great vibe• All volunteer–personal ownership• Video! http://pycon.blip.tv/• 2012 & 2013: Santa Clara• 2014 & 2015: Montreal
  • TiP BoF• Testing in Python Birds of a Feather• Lightning Talks• Heckling...
  • ...and goats
  • The Zen• Guiding principles• Sage advice• >>> import this
  • The Zen• Beautiful is better than ugly. • Readability counts.• Explicit is better than • Special cases arent special implicit. enough to break the rules.• Simple is better than • Although practicality beats complex. purity.• Complex is better than • Errors should never pass complicated. silently.• Flat is better than nested. • Unless explicitly silenced.• Sparse is better than dense.
  • The Zen• In the face of ambiguity, • Although never is often refuse the temptation to better than right now. guess. • If the implementation is• There should be one– hard to explain, its a bad and preferably only one– idea. obvious way to do it. • If the implementation is• Although that way may not easy to explain, it may be a be obvious at first unless good idea. youre Dutch. • Namespaces are one• Now is better than never. honking great idea -- lets do more of those!
  • What About You?
  • Fin• Twitter: @mpirnat• Blog: http://mike.pirnat.com• Win a special prize–name all the movies• Thanks for coming!