34

I am learning Python and am intrigued by the following point in PEP 20 The Zen of Python:

There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch.

Could anyone offer any concrete examples of this maxim? I am particularly interested in the contrast to other languages such as Ruby. Part of the Ruby design philosophy (originating with Perl, I think?) is that multiple ways of doing it is A Good Thing. Can anyone offer some examples showing the pros and cons of each approach. Note, I'm not after an answer to which is better (which is probably too subjective to ever be answered), but rather an unbiased comparison of the two styles.

Paul
  • 103

4 Answers4

51

Compared to languages like Perl, Python has a limited number of control constructs:

  • only if and no unless,
  • only for that iterates over sequences and no foreach or C-style for,
  • only while that checks a condition every loop and no do-while,
  • only if-elif and no switch,
  • there's only one comment construct, the #, and for every line you can tell if it is commented out or not, without looking at previous lines.

Also, there's nearly one way to indent your source; most cases of creative indentation are syntactically excluded.

This makes parsing a Python source easier for humans.

There are attempts to be minimal-but-complete in built-in types and the standard library.

  • for mutable list you use the only built-in list type; it's O(1) for most operations, and you never have to choose the right implementation,
  • for immutable lists, equally, you just use the tuple type,
  • for maps, you use the only built-in dict which is damn efficient in most cases, no need to ponder which implementation to use.

Python 3 extends this to integers: no matter how big your integer is, you use the same type and never care about coercion.

Python tries to avoid syntactic sugar. But sometimes it adds syntactic sugar just to make the obvious way obvious. You can write if foo is not None instead of if not (foo is None) because 'is not' is special-cased. Still foo is not None reads easily, can't be misinterpreted, and you don't have to think, you just write the obvious thing.

Of course, most of more complex things in Python can be done in several ways. You can add methods to classes by declaration or by simple slot assignment, you can pass arguments to functions in a number of creative ways, etc. That's just because the internals of the language are mostly exposed.

The key is that there's always one way which is intended to be the best, the cover-all case. If other ways exist, they were not added as equal alternatives (like if and unless) but merely expose the inner workings. Slowly but steadily such alternatives are obsoleted (not eliminated!) by enhancing the known best mechanism.

Decorators wrap AOP function calls. Before 2.6 you had to use __metaclass__ magic member to declare a class's metaclass; now you can use the same decorator syntax for this, too. Prior to 3.0 you had two sorts of strings, byte-oriented and Unicode, which you could inadvertently mix. Now you have the only Unicode str and the only binary-transparent bytes, which you can't mix by mistake.

9000
  • 24,342
10

Another couple of examples are:
len() is a function instead of a method present in every sequence; if you compare with Java you have .length, .size(), .getSize(), and other methods to find the number of elements in a sequence.

Another example is the fact that .join() is a string method, not a method present in every sequence. You don't need to know if the join parameter is a list, a set, a comprehension, or whatever, it will work.

LYF
  • 3
8

In C there are many possible ways to increase the value of a variable by one:

i++     // Post-increment, returns the number before the increment
++i     // Pre-increment, returns the number after the increment
i += 1 

Each ends up increasing the value of i by 1, but each is slightly different.

In Python, there's really only one way; just add one.

i += 1

And while there's more than one syntactically valid way to do this (e.g. i = i + 1), you're doing the same thing with the same side effects.

6

Another possibility might be list comprehensions. In Python, you could do this:

new_list = []
    for item in list_of_items:
       if item < 10:
           new_list.append(item)

But the "obvious" way (if you're Dutch or are more familiar with Python) of doing this would be with a list comprehension:

new_list = [item for item in list_of_items if item < 10]

It's shorter, the new_list is created in one step, it runs faster I believe, and it is elegant. On the downside, one could argue it feels less explicit, but I think once you get used to it, it is just as explicit.

Inca
  • 1,534
Chelonian
  • 413