Showing posts with label python. Show all posts
Showing posts with label python. Show all posts

2011-08-30

Book Review: Python 3 Web Development Beginner's Guide

I recently received a free review copy (eBook version) of
Cover art for Python 3 Web Development Beginner's Guide
from Packt Publishing. I was looking forward to this book, because I haven't really done much Python 3 work yet, and I wanted to see how it could make my life as a web developer better. However, the book wasn't what I expected. Instead of covering the basics of web development and how Python 3 applies, it is more of an introduction to the sorts of concerns that come up when you build a web framework on top of CherryPy. The sample code just happens to be in Python 3.

The Good

The two best parts of the book, to me, were the coverage of writing a jQuery plugin, and growing an ORM that uses metaclasses to provide a compact, readable way to define the models.

The Bad

I have a rather long list of things I didn't like about the book, some of which are a function of the title setting misleading expectations, and some of which I think are just problematic in general.

In general, I didn't care for the examples. Some of this is personal preference: I find that many people (myself included) learn better when they must type in the examples instead of opening up the code and reading through a completed solution. While the book sometimes indicated that something had been left as an exercise to the reader, opening up the sample code showed that the exercise actually had not been left to the reader. This mismatch between what the text of the book says will be in the sample code and what is actually in the sample code occurs in multiple places throughout the book, and gives a sense that the book was sloppily edited.

I also felt the examples in general were too complicated. It's fine to build up a complicated example over the course of a book, but instead we got a task list, a wiki, a Customer Relationship Management (CRM) tool, a spreadsheet, and more. That's an awful lot to distract you from the beginner's principles that you would expect in a book with this title.

I also didn't care for many of the shortcuts taken in the book. In most instances, the book did acknowledge that the approach taken was not appropriate in the real world, but then proceeded with little or no justification for why it was done the way it was. The two examples that really leap out in this category are the password hashing scheme and the failure to use a template engine.

When the book first introduces authentication, it explains that you should never store passwords in plaintext. This is absolutely correct, but the book then goes on to demonstrate a completely insecure password hashing scheme: UNSALTED SHA1. The author only provides a cursory link to explain what you should actually be doing. In this day and age, demonstrating anything less than a bcrypt-based solution is wrong. Read Enough With The Rainbow Tables and How To Safely Store A Password for a far better explanation than I can provide. There's really no excuse for this: the added complexity of using py-bcrypt instead of writing your own (insecure) SHA1-based solution is trivial at worst; there's a strong case to be made that it would actually be simpler.

The failure to use a template engine (also a weakness acknowledged by the book) really makes the code harder to follow than it should be. Virtually any serious web development effort is going to take advantage of a template engine, and for good reason. This code gives me flashbacks to my days of writing Java servlets before the advent of JSP, and I saw where one other reviewer invoked the specter of PHP. The fact that this style of coding draws such comparisons should give you an idea of just how unpythonic it is. I would be sympathetic to claims of not wanting to add too many external dependencies if the book did not already rely significantly on the magic of jQuery UI.

My last major complaint is simply one of focus: the book spends substantial amounts of time growing an ORM and teaching Python metaclasses (and doing a good job of it), but spends little more than the bare minimum required on CherryPy (which is at the core of the code), and essentially none on understanding HTTP. In fact, the few times it comes up is usually in relation to GET vs. POST, where the decision is usually made based on inane implementation details such as whether request arguments are logged by default instead of HTTP fundamentals such as idempotency, safety, or cacheability (although caching is mentioned elsewhere, in the context of how to prevent it). Also, the book does mention security, but it does not give it the sort of omnipresent emphasis that is necessary to write good web applications, given the hostile nature of the domain. XSS, CSRF, and SQL injection attacks all deserve much more attention than they were given.

The Summary

The book has some good content mixed in with the stuff I didn't like. Unfortunately, the good content is rarely specific to web development. For example, the chapter that uses metaclasses to clean up the ORM is one of the better resources on metaclasses that I've seen, but metaclasses are clearly not specific to web development. Furthermore, the impression of sloppy editing makes it hard to put as much faith in the content as it probably deserves. Given these flaws, I really don't think I'd recommend this book to a friend who was looking to get started with web development.

Back to flipping out...

2010-12-29

Note to Self: Source Django's bash Completion Automatically

If you're using Doug Hellman's awesome virtualenvwrapper to manage your Django projects—and you really should be—try adding the following line to your $VIRTUAL_ENV/bin/postactivate script:


source "$VIRTUAL_ENV/build/Django/extras/django_bash_completion"

If you used the --no-site-packages option to create the virtualenv, that should automatically source the Django bash completion script everytime you workon into your project. If you didn't, you just need to figure out where the Django bash completion script is squirreled away on your system and use that path instead. BTW, --no-site-packages should really be the default.

Back to flipping out...

2009-11-27

Getting Started with distutils

If you've seen some of the discussions surrounding Distribute (a fork of PJE's Setuptools), you may be wondering about distutils, the backbone of both Distribute and Setuptools. If you are, then you may have noticed that it's easier to find details on the various tools built on top of it than it is to find info on the basics, even though distutils is in the standard library.

Helpful Resources

Here are some of the resources I found useful:

Miscellaneous

One thing to remember: the package_data and data_files keyword arguments to setup in setup.py tell the installer what to install, MANIFEST.in tells the packager what to include in the distribution file. Even if it seems duplicative, you need to make sure your resources show up in both places if you want to be able to use python setup.py install using the distribution you build using python setup.py sdist.

Testing It All Out

After you package up your program, you can see if your setup.py is correct by doing a test install in a virtualenv. This let's you make sure you can python setup.py install etc. without contaminating your global site packages. If you haven't heard about virtualenv, I recommend reading up on it; it's really handy. The first page of Google results is a good starting point.

Back to flipping out...

2009-09-22

Sneak Attack: Python is not Java

One of the (in Internet time, at least) golden oldies. Re-reading this makes me cringe when I think back.

Back to flipping out...

Sneak Attack: Why I Like pip

The most concise description I've found of really using pip.

Back to flipping out...

2009-08-29

Sneak Attack: Co-routines as an alternative to state machines

Co-routines as an alternative to state machines. And the money quote: Co-routines are to state machines what recursion is to stacks.

Back to flipping out...

2009-08-25

PyAtl Notes—2009-08-20

What follows is essentially a stream-of-consciousness dump from the PyATL meeting on August 20.

Introduction to Python and Sqlite3

Presented by Alfredo Deza
  • For simple things, it's blazing fast
  • It's local, so no network necessary
  • Serializes to a single file
  • Uses DB-API 2.0 (see PEP 249). Once you have your cursor, you can start running SQL.
  • NOTE: Use sqlite3.connect(':memory:', isolation_level='exclusive') to use an in-memory DB
  • The iterdump() method of the sqlite3 connection gives you an iterable that lets you export the in-memory database. This is handy if you want to write them all to a file.
  • Although the sqlite3 docs claim that the concurrency situation has improved, the Internet at large seems to indicate that problems still abound.

GeoDjango

Presented by Skylar Saveland
  • Recommend you use PostGIS w/ PostgresQL
  • OpenLayers is a nice library for embedding map widgets on any webpage.
  • Core team is available on IRC (#geodjango on Freenode)
  • They make it fairly easy to integrate with Google Maps
  • Instead of standard Django models, you use the one from GeoDjango and then you get the Point, Polygon etc. so that you can store GIS data right with your Django models
  • You can do distance, intersection, touches, etc. queries right in the ORM

Nuts & Bolts

Presented by Brandon Rhodes
  • PyCon is 6 months away

  • String formatting
    • New in 2.6
    • str and unicode objects now have a format() method, and that's the encouraged way to do string formatting; % is right out.
    • At least one of the benefits of the new method is that it's easier to read the name-based format
    • It lets you use dot-notation to access attributes of parameters
    • Also supports indexing into lists, etc.
    • I need to look this up, pronto. It's covered briefly in the sequence types in the standard docs and links to a dedicated page for the new syntax.
  • lxml != ElementTree
    • The goal of ElementTree is to be a Pythonic XML library
    • It's probably a dead project, since the current version is 2 years old
    • lxml uses the "ElementTree" object model, but it's built on top of libxml2 and libxslt so it's blazing fast.
    • Remember these two imports:
      • from lxml import etree
      • from lxml.cssselect import CSSSelector as css
    • lxml also supports CSS selectors
    • Don't forget the default for lxml is an XML parser; you want the HTML parser
    • Ian Bicking says lxml is better than BeautifulSoup

Making Code More Testable

Presented by Brandon Rhodes
  • Based on Writing Testable Code; showing Python examples of how some of these actually look when you put them in action
Don't Mix Object Graph Construction with Application Logic
  • This one makes it really hard to test classes in isolation, because instantiating one starts instantiating a bunch of objects we don't want to test.
  • On a side note, nose handily shows you captured stdout when a test fails, and doesn't show you when the test is successful. That way you can you leave your debugging print statements. It does not capture stderr.
  • The simple fix for this in Python is to accept dependencies in the __init__ method. You might recognize this as constructor-based dependency injection. In Java, mutator-based dependency injection is more common.
Avoid "Global State"
  • Even though every programmer (should) know global state is evil, you still see this quite often in web apps where the global state is something like the request and/or session.
  • The breakage is quite often far more extensive than you would expect, e.g., a test in one file that doesn't handle global state well can break tests in other files, even if they are doing a good job of handling the global state.
  • The fix is to pass the state explicitly, even if the state has to be passed to a lot of functions. This is inconvenient at times, but is usually worth it.

2009-08-24

Project Euler: Problem 20

I finished up Problem 20 a while back. This problem was really straightforward, especially given my work on Problem 16.


import math


if __name__ == '__main__':
    print sum((int(digit) for digit in str(math.factorial(100))))

Back to flipping out...

2009-08-13

Project Euler: Problem 19

I recently solved Problem 19 from Project Euler. Since I don't really enjoy date math, I decided to brute force it and use the datetime module from the standard library.


from datetime import date


def next_first_of_month_in_20th():
    """Generator to list every first of the month during the 20th century."""
    first = date(1901, 1, 1)
    yield first
    while first.year < 2001:
        if first.month == 12:
            first = first.replace(year=first.year + 1)
            first = first.replace(month=1)
        else:
            first = first.replace(month=first.month + 1)
        yield first


def main():
    """
    Solve `Problem 19`_ from Project Euler.
    
    .. _`Problem 19`: http://projecteuler.net/index.php?section=problems&id=19
    """
    return len([first for first in next_first_of_month_in_20th() if first.weekday() == 6])


if __name__ == '__main__':
    print main()

Back to flipping out...

2009-08-07

Project Euler, Problems 18 and 67

I recently finished up Problem 18 (and shortly thereafter, Problem 67). This was a fairly interesting problem, because I like graph theory even if I'm bad at it, and I immediately thought graph theory when I read this problem.

I started creating Vertex and Edge classes, but quickly decided that was too heavyweight for such a small problem. Instead, I chose to use a list of lists where the left-hand child of a node is stored in the next row on the same column and the right-hand child is stored in the next row and the next column.

This made representing and using the data fairly straightforward, but I kept running into the problem of doing an exhaustive search. I knew this was suboptimal (and completely unsuited for the related Problem 67), but I was so locked into thinking of the triangle from the top-down I made things over-complicated.

Eventually, I decided I needed to reset my thinking and Googled for hints. Once I started thinking of the problem from the bottom up (since the graph, after all, is not directed), the solution became clear. Since I didn't need the actual path, I modified the existing data structure to minimize memory requirements.


def find_max_sum(triangle):
    """
    Find the maximum sum for a path from the top to the bottom of ``triangle``.

    >>> test = [[3,], \
            [7, 5], \
            [2, 4, 6], \
            [8, 5, 9, 3]]
    >>> find_max_sum(test)
    23
    """
    while len(triangle) > 1:
        _reduce_triangle(triangle)
    return triangle[0][0]


def _reduce_triangle(to_reduce):
    """
    Reduce ``to_reduce`` in place by rolling up the maximum path info one row.

    Don't return anything to emphasize the in-place nature of this function.

    >>> test = [[3,], \
            [7, 5], \
            [2, 4, 6], \
            [8, 5, 9, 3]]
    >>> _reduce_triangle(test)
    >>> test
    [[3], [7, 5], [10, 13, 15]]
    >>> _reduce_triangle(test)
    >>> test
    [[3], [20, 20]]
    >>> _reduce_triangle(test)
    >>> test
    [[23]]
    """
    last_row = to_reduce[-1]
    for index in xrange(len(to_reduce) - 1):
        to_reduce[-2][index] += max(last_row[index:index + 2])
    del to_reduce[-1]


def main():
    """
    Solve `Problem 18`_ of Project Euler.

    .. _Problem 18: http://projecteuler.net/index.php?section=problems&id=18
    """
    actual = [[75,],
             [95, 64],
             [17, 47, 82],
             [18, 35, 87, 10],
             [20, 4, 82, 47, 65],
             [19, 1, 23, 75, 3, 34],
             [88, 2, 77, 73, 7, 63, 67],
             [99, 65, 4, 28, 6, 16, 70, 92],
             [41, 41, 26, 56, 83, 40, 80, 70, 33],
             [41, 48, 72, 33, 47, 32, 37, 16, 94, 29],
             [53, 71, 44, 65, 25, 43, 91, 52, 97, 51, 14],
             [70, 11, 33, 28, 77, 73, 17, 78, 39, 68, 17, 57],
             [91, 71, 52, 38, 17, 14, 91, 43, 58, 50, 27, 29, 48],
             [63, 66, 4, 68, 89, 53, 67, 30, 73, 16, 69, 87, 40, 31],
             [4, 62, 98, 27, 23, 9, 70, 98, 73, 93, 38, 53, 60, 4, 23]]
    print find_max_sum(actual)


if __name__ == '__main__':
    main()

Starting with this, the solution for Problem 67 is trivial: just write some code to parse the triangle out of a text file instead of coding it directly into the source code:


def find_max_sum(triangle):
    """
    Find the maximum sum for a path from the top to the bottom of ``triangle``.

    >>> test = [[3,], \
            [7, 5], \
            [2, 4, 6], \
            [8, 5, 9, 3]]
    >>> find_max_sum(test)
    23
    """
    while len(triangle) > 1:
        _reduce_triangle(triangle)
    return triangle[0][0]


def _reduce_triangle(to_reduce):
    """
    Reduce ``to_reduce`` in place by rolling up the maximum path info one row.

    Don't return anything to emphasize the in-place nature of this function.

    >>> test = [[3,], \
            [7, 5], \
            [2, 4, 6], \
            [8, 5, 9, 3]]
    >>> _reduce_triangle(test)
    >>> test
    [[3], [7, 5], [10, 13, 15]]
    >>> _reduce_triangle(test)
    >>> test
    [[3], [20, 20]]
    >>> _reduce_triangle(test)
    >>> test
    [[23]]
    """
    last_row = to_reduce[-1]
    for index in xrange(len(to_reduce) - 1):
        to_reduce[-2][index] += max(last_row[index:index + 2])
    del to_reduce[-1]


def _parse_triangle_from_file(data_file):
    """
    Parse out the triangle data from ``data_file``.

    >>> _parse_triangle_from_file('test_triangle.txt')
    [[3], [7, 5], [10, 13, 15]]
    """
    triangle = []
    with open(data_file, 'r') as triangle_file:
        for line in triangle_file:
            triangle.append([int(x) for x in line.split()])
    return triangle


def main():
    """
    Solve `Problem 67`_ of Project Euler.

    .. _Problem 67: http://projecteuler.net/index.php?section=problems&id=67
    """
    print find_max_sum(_parse_triangle_from_file('triangle.txt'))


if __name__ == '__main__':
    main()

2009-06-24

Sneak Attack: Django Circular Model References

Django Circular Model References highlights a neat trick to make Django models play nice with circular references.

Back to flipping out...

2009-06-15

Project Euler: Problem 11, Redux

I've already solved Problem 11, but I didn't really do a great job. As BlueRaja pointed out in a comment, I was doing twice as much work as I needed to. Since I was revisiting this code anyway, I decided to play around a little bit with zip and itertools. Here's the new and improved solution:


"""Solve Problem 11 from Project Euler."""
import itertools
import operator


OFFSET = 3
GRID = [
    [8, 2, 22, 97, 38, 15, 0, 40, 0, 75, 4, 5, 7, 78, 52, 12, 50, 77, 91, 8],
    [49, 49, 99, 40, 17, 81, 18, 57, 60, 87, 17, 40, 98, 43, 69, 48, 4, 56, 62, 0],
    [81, 49, 31, 73, 55, 79, 14, 29, 93, 71, 40, 67, 53, 88, 30, 3, 49, 13, 36, 65],
    [52, 70, 95, 23, 4, 60, 11, 42, 69, 24, 68, 56, 1, 32, 56, 71, 37, 2, 36, 91],
    [22, 31, 16, 71, 51, 67, 63, 89, 41, 92, 36, 54, 22, 40, 40, 28, 66, 33, 13, 80],
    [24, 47, 32, 60, 99, 3, 45, 2, 44, 75, 33, 53, 78, 36, 84, 20, 35, 17, 12, 50],
    [32, 98, 81, 28, 64, 23, 67, 10, 26, 38, 40, 67, 59, 54, 70, 66, 18, 38, 64, 70],
    [67, 26, 20, 68, 2, 62, 12, 20, 95, 63, 94, 39, 63, 8, 40, 91, 66, 49, 94, 21],
    [24, 55, 58, 5, 66, 73, 99, 26, 97, 17, 78, 78, 96, 83, 14, 88, 34, 89, 63, 72],
    [21, 36, 23, 9, 75, 0, 76, 44, 20, 45, 35, 14, 0, 61, 33, 97, 34, 31, 33, 95],
    [78, 17, 53, 28, 22, 75, 31, 67, 15, 94, 3, 80, 4, 62, 16, 14, 9, 53, 56, 92],
    [16, 39, 5, 42, 96, 35, 31, 47, 55, 58, 88, 24, 0, 17, 54, 24, 36, 29, 85, 57],
    [86, 56, 0, 48, 35, 71, 89, 7, 5, 44, 44, 37, 44, 60, 21, 58, 51, 54, 17, 58],
    [19, 80, 81, 68, 5, 94, 47, 69, 28, 73, 92, 13, 86, 52, 17, 77, 4, 89, 55, 40],
    [4, 52, 8, 83, 97, 35, 99, 16, 7, 97, 57, 32, 16, 26, 26, 79, 33, 27, 98, 66],
    [88, 36, 68, 87, 57, 62, 20, 72, 3, 46, 33, 67, 46, 55, 12, 32, 63, 93, 53, 69],
    [4, 42, 16, 73, 38, 25, 39, 11, 24, 94, 72, 18, 8, 46, 29, 32, 40, 62, 76, 36],
    [20, 69, 36, 41, 72, 30, 23, 88, 34, 62, 99, 69, 82, 67, 59, 85, 74, 4, 36, 16],
    [20, 73, 35, 29, 78, 31, 90, 1, 74, 31, 49, 71, 48, 86, 81, 16, 23, 57, 5, 54],
    [1, 70, 54, 71, 83, 51, 54, 69, 16, 92, 33, 48, 61, 43, 52, 1, 89, 19, 67, 48]
]
INVALID_COORDS_PRODUCT = 0


def _calc_down(row_index, col_index):
    """Calculate the product of the sequence going straight down from ``row_index``."""
    cols = itertools.repeat(col_index, OFFSET + 1)
    last_row = min(row_index + OFFSET + 1, len(GRID))
    coords = zip(xrange(row_index, last_row), cols)
    values = (GRID[row][col] for row, col in coords)
    return reduce(operator.mul, values)


def _calc_right(row_index, col_index):
    """Calculate the product of the sequence going straight right from ``col_index``."""
    rows = itertools.repeat(row_index, OFFSET + 1)
    last_col = min(col_index + OFFSET + 1, len(GRID[row_index]))
    coords = zip(rows, xrange(col_index, last_col))
    values = (GRID[row][col] for row, col in coords)
    return reduce(operator.mul, values)


def _calc_up_right(row_index, col_index):
    """Calculate the product of the sequence going up and to the right from (``row_index``, ``col_index``)."""
    last_row = max(row_index - OFFSET - 1, -1)
    last_col = min(col_index + OFFSET + 1, len(GRID[row_index]))
    coords = zip(xrange(row_index, last_row, -1), xrange(col_index, last_col))
    values = (GRID[row][col] for row, col in coords)
    return reduce(operator.mul, values)


def _calc_down_right(row_index, col_index):
    """Calculate the product of the sequence going down and to the right from (``row_index``, ``col_index``)."""
    last_row = min(row_index + OFFSET + 1, len(GRID))
    last_col = min(col_index + OFFSET + 1, len(GRID[row_index]))
    coords = zip(xrange(row_index, last_row), xrange(col_index, last_col))
    values = (GRID[row][col] for row, col in coords)
    return reduce(operator.mul, values)


def _get_max_product_for_coordinate(row, col):
    """Find the maximum product achievable from (``row``, ``col``)."""
    return max(_calc_up_right(row, col), _calc_right(row, col), _calc_down_right(row, col), _calc_down(row, col))


def problem_11():
    """Find the largest product of four adjacent elements."""
    products = set()
    for row in range(len(GRID)):
        for col in range(len(GRID[row])):
            products.add(_get_max_product_for_coordinate(row, col))
    return max(products)


if __name__ == '__main__':
    print problem_11()

Back to flipping out...

2009-06-06

Review of Expert Python Programming, Part Four

Chapter 5: Writing a Package

A Common Pattern for All Packages

This section provides all the details necessary to create a namespaced package that consists of multiple eggs glued together by a master egg using distutils and setuptools. There are multiple subsections that cover everything you should need to build your project, register it with the Cheeseshop (or any other package index), and upload it for the world to enjoy.

How to Uninstall a Package

This section provides a short rationalization for why there isn't a built-in uninstall command and the current workaround. It's not mentioned in the book, but the author is currently working on improving distutils, and an uninstall command is on the agenda. The full details are in the Adding an Uninstall Function section of PEP 376.

The Template-Based Approach

After covering the basics of building and distributing a package in the previous section, this section focuses on using templates to reduce the tedium in creating consistent application skeletons. The tool used in this section is Python Paste. There are a couple of short examples using pre-existing templates.

Creating the Package Template

This section covers the process of creating a custom template that describes the structure introduce in the first section.

The rest of the chapter just covers generic aspects of the development cycle, such as version numbering, so I will skip it for this review.

Back to flipping out...

Review of Expert Python Programming, Part Three

Chapter 4: Choosing Good Names

This chapter is aimed at helping improve API design, mostly by helping improve the names you choose when building the API. Ironically enough, not all of the advice is about naming, so the chapter title could be better. Most of the advice is good, although some of it overlaps with PEP 8 and some of it is far more general than just Python. That doesn't dilute the value of the advice, but I will skip it in this review.

Best Practices for Arguments
Build Arguments by Iterative Design

This advice is applicable to pretty much any language, but it is surprising how many people get it wrong. Also, if you're new to languages that allow you to specify default values for parameters, this section highlights one of the best uses for that feature.

Trust the Arguments and Your Tests

More solid advice: don't try to recreate a static typing system in a dynamically typed language. This isn't exactly profound insight, but a lot of people (myself included) make this mistake when they transition to dynamic typing from static typing. Instead of just saying "the tests will catch that", which some interpret as "write unit tests and/or assertions to enforce static-style typing", the author is careful to explain that the tests are supposed to test actual use cases. The author also mentions Design-by-Contract. While he doesn't seem to favor it, he does provide a link to Contracts for Python. It should be noted that the related PEP 316 has been deferred, so some people may find the resulting code "unpythonic".

Use *args and **kw Magic Arguments Carefully

The author acknowledges that use of *args and **kw (also seen as **kwargs) is sometimes necessary, e.g., metaprogramming, but in general considers them a design smell. He also offers advice for improving the design, e.g., accepting a single iterable parameter instead of *args.

Module and Package Names

Mostly mundane and duplicative of PEP 8, but there are a couple of gems: the convention of using a lib suffix in a module or package name if it is implementing a protocol, e.g., smtplib; using __init__ to import some APIs into the top level of the package, including a caveat about the increased potential of circular dependencies.

Working on APIs
Tracking Verbosity

Even though this is the sort of generic advice I said I was going to skip, I liked it so much I decided to include it anyway. If there is a common use case for some sequence of calls into your API, you should expose a function that encapsulates it. Doing anything else just invites errors.

Building the Namespace Tree

I really liked this section. In just a couple of short pages, the author shows an example of evolving the namespace structure for an application. I don't have enough experience in the Python world to know if this is a realistic example, but I thought it was presented well and made good sense. I also like that the book addresses this topic at all. The Zen of Python says "Namespaces are one honking great idea -- let's do more of those!" but I don't see many mentions of resources covering how to design them well.

Using Eggs

This sections provides a concise explanation of what eggs do and a sneak preview at how to define them. I would have liked more info, but the inset promises more details in Chapter 6.

Using a Deprecation Process

The advice in this section is also generic (don't break an already-published API), but I mentioned it because it shows the Pythonic method for deprecating old APIs: DeprecationWarning.

Useful Tools

This section only lists two tools: Pylint and CloneDigger. They are useful, but some tips for using them to greatest effect would have been nice.

Back to flipping out...

2009-06-01

Sneak Attack: Introduction to Python Profiling (PyCon talk)

This PyCon talk on profiling is really good stuff. Also, it's nice that he points to KCachegrind and RunSnakeRun for better visualizations than the default from cProfile.

Back to flipping out...

Five Things I Hate (or at least dislike) About Python

1. Implicit Variable Creation

This is probably my biggest complaint. Why would you want to implicitly create a variable the first time something is assigned to it? This might make sense in a language where variables are immutable by default (after all, you only ever assign a variable once), but Python isn't. Also, I realize there is a technical difference between rebinding names and changing the value of a variable; I don't find that particular distinction useful here.

2. Dearth of Collections (in the standard library)

Don't get me wrong: defaultdict and namedtuple are nice, but on occasion I really find myself wishing for some more advanced data structures, e.g., Red-Black tree. I'm not even talking about probabilistic structures like Bloom filters or skip lists.

3. Lack of Tail-call Elimination

I know it likely won't happen, but I still wish I had it. To me (and I'm sure many others), recursive algorithms are the most natural way to express certain algorithms, e.g., traversing a tree. I can do it using a loop, but it really drops me out of the zone.

4. Concurrency in the Standard Library

In an ideal world, Python would support concurrency on a level with first-class functions, similar to Erlang. It's almost not even fair to ding Python on this, since pretty much every other language its age has the same problem, but a man can dream, right? At least the multiprocessing module made it into the standard library.

5. Interfaces

It would be really swell if Python had support for something like interfaces. I know that PEP 3119 introduced Abstract Base Classes, so this one is probably on the way to being remedied, but the feature is so new I haven't yet encountered it in the wild.

Back to flipping out...

2009-05-29

Sneak Attack: Installing cx_Oracle on an Intel Mac

Install instructions that actually work for cx_Oracle on an Intel Mac. It requires MacPorts and doesn't work with the latest version (5.0.2) of cx_Oracle, so make sure you actually use 4.4.1.

Back to flipping out...

2009-05-23

Review of Expert Python Programming, Part Two

Chapter 3
Subclassing Built-in Types
  • I started learning Python after this was added, so it never occurred to me not to do this.
Accessing Methods from Superclasses
  • Tries to explain super, but it's quite confusing (mostly due to the multiple-inheritance problems).
  • The standard docs do a better job of making it clear the main benefit is making maintenance easier in single-inheritance examples.
Understanding Python's Method Resolution Order (MRO)
The section isn't as clear as it could be, but it has solid information. It explains what the MRO is used for and how it's different between 2.2 and 2.3, and the __mro__ attribute
super Pitfalls
Points out some of the most common problems with super: mixing super and classic calls (and how to use __mro__ to choose what to do) and subclass constructors that take arguments that differ from their parent classes.
Best Practices
  • Solid, short (so you can remember it) section.
Descriptors and Properties
Descriptors
This section isn't overly clear. It describes what descriptors are from a technical perspective, but doesn't do a great job of explaining why you'd want to use them. Also, a fair number of the examples have errors, e.g., code for setting values when the text says it is for reading values. Luckily, it contains a link to the (more helpful) How-To Guide for Descriptors.
Properties
This section explains property and the property attributes it returns (though not necessarily why they're so useful) and does a good job of pointing out some gotchas, e.g., the way they don't pick up overridden methods. The solution offered is perfectly sensible: override the property instead of the (typically private) method bound to fget.
Slots
  • Short but informative section on slots, which I haven't seen mentioned before.
Meta-Programming
The __new__ Method
This section covers __new__ and its usefulness for making sure that a class's invariants aren't violated because a subclass didn't explicitly invoke __init__.
The __metaclass__ method
This section covers customizing class creation using __metaclass__ and points out that, in most cases, there are easier-to-understand alternatives. One example of when there isn't is adjusting read-only attributes, e.g., the __doc__ attribute of the built-in metaclass type. Other suggested usages for __metaclass__ include frameworks enforcing behavior across large groups of classes and orthogonal functionality such as logging. The section closes with a link to A Primer on Python Metaclass Programming.
Summary

The summary section is a short, bulleted list highlighting the most important points made in the chapter. Again, it's short enough to be easily memorable.

Back to flipping out...