Resources/pointers for writing maintable, testable Python

Chris Angelico rosuav at gmail.com
Wed May 18 23:09:23 EDT 2016


On Thu, May 19, 2016 at 11:15 AM, Jacob Scott <jacob.scott at gmail.com> wrote:
> I think I would be (perhaps pleasantly) surprised if there was a wide gulf
> between Python 2.7 and Python 3 *in terms of advice/resources applicable to
> my original question*. Based on my (admittedly shallow) understanding of
> overall Python 2.7/3 differences, the biggest changes (from e.g.
> http://sebastianraschka.com/Articles/2014_python_2_3_key_diff.html) tend to
> be a bit lower level (utf-8 str) than what I'm focused on (maintainable and
> testable classes, functions, modules, etc).

It's not really about UTF-8 (and the article you link to is flat wrong
in describing Py3's str type as UTF-8); it's about the recognition of
a fundamental difference between text and bytes. Looking at it another
way: Python 2 lets you pretend it's all easy, so long as you're
working with the simple Latin letters A-Z and a-z, but when you need
anything else (other alphabets, non-ASCII symbols like dashes and
quotes and emoji, diacritical marks, etc, etc), Python 2 doesn't help
you at all, and suddenly you're on your own. Python 3 forces you to
think up-front about things (a little bit of extra work/hassle), but
then gives you all the world's languages for free. That's a very high
level feature: you ask a user to enter his/her name, and instead of
requiring that it be English without diacritical marks, you can accept
(almost[1]) anything. Sometimes, UTF-8 lets you *pretend* that Python
2 (or C or PHP or whatever other language you pick) can handle all
those other characters too, but it's a massively leaky abstraction.

The other broad change in Python 3 is a shift in focus from concrete
lists to lazy objects. In Py2, range() returns a list of numbers; in
Py3, it returns a "range object", which functions just like that list
in many ways (subscripting, iterating, etc work the same way), but is
far more memory-efficient on huge ranges. In Py2, a dictionary's
keys() method returns a list; in Py3, it returns a special view
object. That kind of thing.

Quite a few of the other differences mentioned in that article are
actually the removal of already-deprecated syntax. For instance, the
'except NameError as err:' syntax works in 2.6 and 2.7 as well as 3.x,
and is the recommended way to spell it (unless you need to support
ancient Pythons - 2.5 is pretty old now, but some people do support
it). For backward compatibility, all of the 2.x line still supports
the old syntax, but new code should be using the reliable syntax - the
comma is ambiguous (what if you wanted to accept more than one
exception type?). Similarly, you shouldn't be calling an iterator's
.next() method directly; always call the built-in next() function.
Python 3 made this a lot clearer by renaming the method to __next__(),
which clarifies that this is a "magic method" or "special method.
Dunder methods are for defining, not calling. (Rule of thumb, not
entirely true, but close on.)

For the rest, Python 3 basically has the advantage of six additional
years of development time. That's given us features like exception
chaining, core support for virtual environments, a variety of new
stdlib modules, support for asynchronous I/O, a new multiplication
operator, and soon (3.6), interpolated string 'literals' (more like a
list comprehension than a string literal). And that gap is ever
widening. Using 2.7 means abandoning all those features. It's still in
support, but it's bug fixes and security patches only - which, as the
Red Queen explained to Alice, is where you have to run as fast as you
can just to stay in one place.

ChrisA

[1] Some people's names can't be represented in Unicode. But excluding
those names is a lot less significant than excluding the huge
proportion of people whose names aren't representable in ASCII.



More information about the Python-list mailing list