<?xml version='1.0'?><!DOCTYPE slideshow SYSTEM "xslides.dtd"><slideshow>
<title>Python for Perl Hackers</title>
<author>Mark Eichin, SIPB</author>
<date>IAP 2004</date>
<slide>
<header>Why are you here?</header>
<bullet>perl approaches line noise if not disciplined</bullet>
<bullet>perl is subtle and quick to anger</bullet>
<bullet>code is read more than written</bullet>
<bullet>power is still important</bullet>
</slide>
<slide>
<header>Who is this guy?</header>
<bullet>perl since perl 3 (ref crypto)</bullet>
<bullet>author of perl journal x-in-perl</bullet>
<bullet>sipb, -c help</bullet>
<bullet>other startup perl stuff</bullet>
</slide>
<slide>
<header>Why</header>
<bullet>How I got here</bullet>
<subbullet> qmtest</subbullet>
<subbullet> some code at work</subbullet>
<subbullet> indentation</subbullet>
<bullet>why evangelize</bullet>
</slide>
<slide>
<header>Rigor</header>
<bullet>Compare construct by construct</bullet>
<bullet>Especially if you had real metrics for readability</bullet>
<bullet>Possibly based on classes of error</bullet>
<bullet>All you're getting tonight is anecdotes and examples</bullet>
</slide>
<slide>
<header>General Readability</header>
<bullet>whitespace just isn't that hard to work with</bullet>
<bullet>comment-formatting tool</bullet>
<raw>Tools/scripts/pindent.py</raw>
<raw>def foobar(a, b):</raw>
<raw>   if a == b:</raw>
<raw>       a = a+1</raw>
<raw>   else:</raw>
<raw>       print 'oops!'</raw>
<raw>   # end if</raw>
<raw># end def foobar</raw>
</slide>
<slide>
<header>More Readability</header>
<bullet>common string functions: startswith, endswith, strip,</bullet>
<subbullet>lower, replace...</subbullet>
<bullet>even one liners can be clear</bullet>
<raw>perl -e 'print join("\n",@INC)'</raw>
<raw>python -c 'import sys; print sys.path'</raw>
<raw>python -c 'import sys; print "\n".join(sys.path)'</raw>
</slide>
<slide>
<header>Subroutines</header>
<bullet> perlintro:</bullet>
<raw>       sub log {</raw>
<raw>           my $logmessage = shift;</raw>
<raw>           print LOGFILE $logmessage;</raw>
<raw>       }</raw>
<bullet> in python:</bullet>
<raw>    def log(logmessage):</raw>
<raw>       print &gt;&gt; LOGFILE, logmessage</raw>
<bullet>subroutines have arguments, not an afterthought</bullet>
<bullet>local (not perl-local, perl-my) variables</bullet>
<bullet>later example:</bullet>
<raw>       my ($logmessage, $priority) = @_;       # common</raw>
<subbullet>does even less about noticing caller errors</subbullet>
</slide>
<slide>
<header>Modules</header>
<bullet>The simplest module mentioned in perlnewmod:</bullet>
<raw>package Text::Tabs;</raw>
<raw>require Exporter;</raw>
<raw>@ISA = (Exporter);</raw>
<raw>@EXPORT = qw(expand unexpand $tabstop);</raw>
<raw>use vars qw($VERSION $tabstop $debug);</raw>
<raw>$VERSION = 98.112801;</raw>
<bullet>'package' and Exporter bits just go away</bullet>
<bullet>there isn't a version convention</bullet>
<bullet>there is a __doc__ convention, just put in string</bullet>
<bullet>a top level __version__ would server</bullet>
<bullet>err, .expandtabs is a string method anyway</bullet>
</slide>
<slide>
<header>Module Hierarchy</header>
<bullet>don't care if you inherit from</bullet>
<bullet>just care that you provide readline.</bullet>
<bullet>namespace protection is there</bullet>
<bullet>But, you can use from Foo import * to get around it</bullet>
<bullet>(if it really makes things more readable.)</bullet>
<bullet>explicit export-list control is available</bullet>
</slide>
<slide>
<header>Exceptions</header>
<bullet>try/except/finally</bullet>
<bullet>raise (old string form, superceded by class-form)</bullet>
<bullet>base-class-except lets you have specialized exceptions</bullet>
<raw>class FooWarning(Warning): pass</raw>
<raw>class BarError(Error): pass</raw>
<raw>try: this</raw>
<raw>except Warning: whine()</raw>
</slide>
<slide>
<header>Pack/Unpack</header>
<bullet>"import struct" and struct.pack, struct.unpack</bullet>
<bullet>explicit struct.calcsize!</bullet>
<bullet>Arguments actually have to match</bullet>
<bullet>native vs. endian is modifier, not encoded in args</bullet>
<bullet>explicit native vs. little vs. big.</bullet>
</slide>
<slide>
<header>Unicode</header>
<bullet>"just there" since 1.6</bullet>
<bullet>perl needs a "use charnames;" pragma</bullet>
<bullet>possibly with the ":full" tag</bullet>
<bullet>and at least perl 5.6</bullet>
</slide>
<slide>
<header>Hashes</header>
<bullet>"dictionaries", or "dicts"</bullet>
<bullet>d.keys(), d.values(), d.items()</bullet>
<bullet>vs. keys(%d)</bullet>
<bullet>"tied hashes": class with __getitem__</bullet>
<bullet>dbmopen/DB-tied hashes: "import shelve"</bullet>
</slide>
<slide>
<header>String Interpolation</header>
<bullet>"$foo" is pretty powerful</bullet>
<bullet>except when it doesn't work at all</bullet>
<subbullet>(hashes with quoted string key)</subbullet>
<bullet>Python uses a % operator (C++ STL-like)</bullet>
<bullet>Usual tricks (%s prints pretty much anything)</bullet>
<bullet>named element lookup</bullet>
<raw>&gt;&gt;&gt; "%(hi)s" % { "foo": 2, "hi": "world"}</raw>
<raw>'world'</raw>
<bullet>combined with locals(), globals() can be excessive</bullet>
</slide>
<slide>
<header>Regexps</header>
<bullet>most of what you'd expect</bullet>
<bullet>python has named groups</bullet>
<bullet>perl only has EXPERIMENTAL "(?{ code })"</bullet>
<raw>$_ = "The brown fox jumps over the lazy dog";</raw>
<raw>/the (\S+)(?{ $color = $^N }) (\S+)(?{ $animal = $^N })/i;</raw>
<raw>print "color = $color, animal = $animal\n";</raw>
<bullet>vs.</bullet>
<raw>s = "The brown fox jumps over the lazy dog"</raw>
<raw>m = re.search("the (?P&lt;color&gt;\S+) (?P&lt;animal&gt;\S+)", s, re.I)</raw>
<raw>print "color = %(color)s, animal = %(animal)s" % m.groupdict()</raw>
</slide>
<slide>
<header>Loop Constructs</header>
<raw>for thing in things_to_search:</raw>
<raw>   if predicate(thing):</raw>
<raw>       do_something_with(thing)</raw>
<raw>       break</raw>
<raw>else:</raw>
<raw>   print "didn't find a thing that is predicately"</raw>
<bullet>which is often what you *mean*</bullet>
</slide>
<slide>
<header>C modules:</header>
<bullet>Not too hard in either</bullet>
<bullet>python seems to keep the parts in one place a bit more</bullet>
<bullet>yet another zephyr module.</bullet>
<bullet>Surprisingly little need for them so far</bullet>
</slide>
<slide>
<header>Batteries Included</header>
<bullet>Builtin modules that everyone will have</bullet>
<bullet>urllib and gzip</bullet>
<bullet>"os" package is quite rich for posix, at least.</bullet>
</slide>
<slide>
<header>Aspect Oriented</header>
<bullet>Desperately hookable</bullet>
<bullet>stuff fixes in to existing classes</bullet>
<raw>import gzip</raw>
<raw>save_init_read = gzip.GzipFile._init_read</raw>
<raw>def fix_init_read(self):</raw>
<raw>    save_init_read(self)</raw>
<raw>    self.size = long(self.size)</raw>
<raw>gzip.GzipFile._init_read = fix_init_read</raw>
<bullet>make unconfigurable logging functions shut up</bullet>
</slide>
<slide>
<header>Wrapping</header>
<raw>class Collections(CollectionsRaw):</raw>
<raw>    """Locking wrappers around raw collection functions"""</raw>
<raw>    def __init__(self, *args):</raw>
<raw>        CollectionsRaw.__init__(self, *args)</raw>
<raw>        # operations that need locking</raw>
<raw>        self.reserve_name = self.lock_wrapper(CollectionsRaw.reserve_name)</raw>
<raw>    def lock_wrapper(self, wrapped_fn):</raw>
<raw>        def lock_inner(*args):</raw>
<raw>            self.lock()</raw>
<raw>            try:</raw>
<raw>                self.update()</raw>
<raw>                wrapped_fn(self, *args)</raw>
<raw>                self.flush()</raw>
<raw>            finally:</raw>
<raw>                self.unlock()</raw>
<raw>        return lock_inner</raw>
<raw>    def lock(self):</raw>
<raw>        self.lockfile.lock()</raw>
</slide>
<slide>
<header>Numbers</header>
<bullet>perl: BigInt etc. classes</bullet>
<bullet>python: builtin "small" integers (32 bitsigned)</bullet>
<bullet> and "long" (arbitrary length) integers</bullet>
<bullet>also (double) floats</bullet>
<bullet>2.2 and later, int to long autopromotion</bullet>
</slide>
<slide>
<header>Other</header>
<bullet>"sequence unpacking": sequences can assign piecewise:</bullet>
<raw>size, value = fun(path, op)</raw>
<raw>for k,v in d.items(): n[k.lower()] = v</raw>
</slide>
<slide>
<header>References</header>
<url>http://www.python.org/pypi</url>
<url>http://mechanicalcat.net/cgi-bin/log/python/anti-pitfalls.html</url>
<url>http://www.mit.edu/iap/2004/python-for-perl/index.html</url>
<url>http://zephyrfalcon.org/weblog/arch_d7_2003_10_25.html#e387</url>
<url>sipb-iap-python-for-perl@mit.edu</url>
<url>http://www.thok.org/intranet/python/index.html</url>
</slide>
<slide>
<header>Appendix: sample slide</header>
<raw>= Pack/Unpack</raw>
<raw>* "import struct" and struct.pack, struct.unpack</raw>
<raw>* explicit struct.calcsize, which I've always had to kludge.</raw>
<raw>* Arguments actually have to match</raw>
<raw>* native vs. endian is modifier, not encoded in the individual args</raw>
<raw>* explicit native vs. little vs. big.</raw>
</slide>
<slide>
<header>Appendix: code</header>
<raw>#!/usr/bin/python</raw>
<raw>import sys</raw>
<raw>def entity_quote(txt):</raw>
<raw>    return txt.replace("&amp;", "&amp;amp;").replace("&lt;", "&amp;lt;").replace("&gt;", "&amp;gt;")</raw>
<raw>def wrap(elem, txt):</raw>
<raw>    return "&lt;%s&gt;%s&lt;/%s&gt;" % (elem, entity_quote(txt), elem)</raw>
</slide>
<slide>
<header>Appendix: one slide</header>
<raw>def do_a_slide(f):</raw>
<raw>    print "&lt;slide&gt;"</raw>
<raw>    for line in f:</raw>
<raw>        if line == "\n":</raw>
<raw>            print "&lt;/slide&gt;"</raw>
<raw>            break</raw>
<raw>        elif line.startswith("= "):</raw>
<raw>            print wrap("header", line[2:].strip())</raw>
<raw>        elif line.startswith("* "):</raw>
<raw>            print wrap("bullet", line[2:].strip())</raw>
<raw>        elif line.startswith("@ "):</raw>
<raw>            print wrap("url", line[2:].strip())</raw>
<raw>        else:</raw>
<raw>            sys.exit("Slide Huh?" + line)</raw>
<raw>    else:</raw>
<raw>        raise EOFError()</raw>
</slide>
<slide>
<header>Appendix: header</header>
<raw>def do_a_header(f):</raw>
<raw>    print "\n".join(["&lt;?xml version='1.0'?&gt;"</raw>
<raw>                     '&lt;!DOCTYPE slideshow SYSTEM "xslides.dtd"&gt;'</raw>
<raw>                     '&lt;slideshow&gt;'])</raw>
<raw>    for line in f:</raw>
<raw>        if line == "\n":</raw>
<raw>            break</raw>
<raw>        elif line.startswith("TITLE: "):</raw>
<raw>            print wrap("title", line.lstrip("TITLE:").strip())</raw>
<raw>        elif line.startswith("AUTHOR: "):</raw>
<raw>            print wrap("author", line.lstrip("AUTHOR:").strip())</raw>
<raw>        elif line.startswith("DATE: "):</raw>
<raw>            print wrap("date", line.lstrip("DATE:").strip())</raw>
<raw>        else:</raw>
<raw>            sys.exit("Header Huh?" + line)</raw>
</slide>
<slide>
<header>Appendix: footer, main</header>
<raw>def do_a_footer(f):</raw>
<raw>    print "\n".join(["&lt;/slide&gt;", "&lt;/slideshow&gt;"])</raw>
<raw>if __name__ == "__main__":</raw>
<raw>    f = sys.stdin</raw>
<raw>    do_a_header(f)</raw>
<raw>    try:</raw>
<raw>        while 1:</raw>
<raw>            do_a_slide(f)</raw>
<raw>    except EOFError:</raw>
<raw>        do_a_footer(f)</raw>
</slide>
</slideshow>

