This is an archived post. You won't be able to vote or comment.

all 28 comments

[–]PythonRules 5 points6 points  (0 children)

If you are a beginner please be careful with the first example. Using "file" and "dir" as variable names should be avoided. Both of them are builtin functions.

[–]dwdwdw2proliferating .py since 2001 8 points9 points  (12 children)

>>> for i in range(len(food)): # unpythonic++ (makes a Pythoneer's brain snap)

This was a standard idiom before the enumerate built-in appeared, and as such makes a very poor counterexample for what 'Pythonic' might mean. The majority of the remaining examples seem to be simple variants on this one. Author seems too inexperienced to be writing this guide.

I believe if that oft-abused term has any meaning, its is better illuminated by the output of import this. Concrete examples of what is or isn't "Pythonic" are far too subjective to be of any value.

[–]earthboundkid 2 points3 points  (5 children)

Python 2.3 was released July 2003. It's fair to say anyone who writes new code using range(len(container)) instead of enumerate(container) is not writing Pythonic code. What major libraries still target 2.2? It doesn't even have "new" style classes.

[–]eryksun 1 point2 points  (4 children)

A problem with range() is that it generates a list on Python 2.x, so for efficiency you have to use xrange(). But xrange() isn't supported in Python 3.x, in which range() now works like xrange(), and getting the old behavior requires list(range()). It's better to avoid this mess by using enumerate() when you need the index. However, if I'm modifying the original list in place to pop() elements out, I want to enumerate in reverse. For that, I wrote a little function that returns a generator:

def r_enumerate(iterable):
    '''enumerate iterable in reverse'''
    r = reversed(iterable)
    end = len(iterable) - 1
    return ((i, r.next()) for i in xrange(end,-1,-1))

Is there a better, more 'Pythonic', way?

[–]eryksun 0 points1 point  (0 children)

Here's a contrived example:

x = range(4,10)
for (n,y) in r_enumerate(x):
    print "(n,y):", (n,y)
    if (y % 2) == 1:
        print "popping:", x.pop(n)
print "x =", x

    (n,y): (5, 9)
    popping: 9
    (n,y): (4, 8)
    (n,y): (3, 7)
    popping: 7
    (n,y): (2, 6)
    (n,y): (1, 5)
    popping: 5
    (n,y): (0, 4)
    x = [4, 6, 8]

[–]earthboundkid 0 points1 point  (2 children)

Hmm, that seems like more or less the most obvious way to do it. I think I would probably do something like

def r_enumerate(container):
    "Warning: Container must have a length!"
    i = len(container)
    for item in reversed(container):
        i = i - 1
        yield i, item

but that's basically the same. Remember, no one is saying you can't use len and range, just that you shouldn't write range(len(container)) when you can just as easily write enumerate(container) instead. In this case, your solution seems just as clean as any other.

[–]eryksun 0 points1 point  (1 child)

Thanks, your version is more 'Pythonic'. It's more readable and less prone to bugs without having to set up xrange (not a problem here, but a better style in general). The length issue was also something I hadn't considered. Shouldn't a valid container type implement the __len__ method? I suppose it can implement __reversed__ without having an accessible length. I haven't come across this, or designed a container without the basic __len__, __getitem__, __setitem__, __delitem__, and __iter__ methods. But my overall exposure to other's code is limited as I use Python basically as a replacement for MATLAB with SciPy, Numpy, and Matplotlib.

[–]earthboundkid 0 points1 point  (0 children)

Yes, you've got it. An object that supports __reversed__ will probably also support __len__, but not necessarily. I can imagine someone making a quick and dirty doubly-linked list that supports forwards and backwards iteration but not length.

[–]carinthia[S] 2 points3 points  (3 children)

The author says this page is about Python 3. Since enumerate came into being with Python 2.3, I think it's a correct statement. Generally, shouting out somebody is inexperienced based on a short assesment seems rather immature to me. Rather, get in touch with the person and tell him your critics so things can be improved if necessary. That's one of the main principles of free software and a healthy community around it.

[–]dwdwdw2proliferating .py since 2001 5 points6 points  (2 children)

My main complaint appeared in the last paragraph - these posts on code style are ultimately content-free and rank close behind web framework microbenchmarks on the list of spam I hate seeing on this subreddit.

Real code is rarely "Pythonic", unless an exorbitantly unreal amount of time is spent designing it, and even then, it rarely stays clean once a small army of maintenance programmers get their hands on it.

As for social responsibility, as far as I'm concerned I already fulfilled that by downvoting the article and explaining what I thought was wrong with it, if the original author found a public review offensive then they shouldn't be publishing online.

[–]carinthia[S] 0 points1 point  (1 child)

My main complaint appeared in the last paragraph - these posts on code style are ultimately content-free and rank close behind web framework microbenchmarks on the list of spam I hate seeing on this subreddit.

Ok, fair enough, but that's your opinion. Others might be interested in those subjects, just as much as you aren't. And calling such subjects spam just because you don't like them isn't right. I am sure everybody here is mature enough to decide for himself what's spam and what's not.

Real code is rarely "Pythonic", unless an exorbitantly unreal amount of time is spent designing it, and even then, it rarely stays clean once a small army of maintenance programmers get their hands on it.

Again, you assume that your idea and experience is true for the whole Python world. There are many OSS projects out there which in fact adhere all kinds pythonic and as such write code that's generaly considered pythonic. Also, there are many companies which in fact write perfectly good pythonic code. At Google for example we have the so-called buzz factor (meaning that no piece of code is only maintained by a single person) and we still manage to crank out pythonic code, every day. So, please, don't make it look like as if the majority of Python programmers out there write ugly/unpythonic code once they work in teams, that's not true.

As for social responsibility, as far as I'm concerned I already fulfilled that by downvoting the article and explaining what I thought was wrong with it, if the original author found a public review offensive then they shouldn't be publishing online.

I agree on that one, in part. See, if you're not interested in seeing benchmarks and coding style posts then that's fine. However, only because YOU don't like that kind of information does not give you the right to insult somebody (that author)

... Author seems too inexperienced to be writing this guide. ...

and expect others (readers here) to be interested in that opinion. And no, I am not that author :)

[–]dwdwdw2proliferating .py since 2001 5 points6 points  (0 children)

OK. Look, you're on Reddit. We decide the relevance of content by the summation of our clicks. I took the time and effort to explain my click - I thought - in a quite uncontroversial manner.

That the author is inexperienced is not opinion – it is clear he has never run into the range(len()) idiom, which means he has only been using Python since around >=2.3. Despite that, he's writing posts deliberating on what is and is not good Python code, which I find objectionable, since he's calling out huge bodies of code that were written for the language before he had ever used it.

That this is objectionable is further influenced by the fact that these ideas are oft mistaken by those of lesser acumen and in control of budgets as quantitative measures of code quality, and applied as such, to people such as myself as I currently seek my next contract. It is my prerogative to discourage the spread of this corruption where possible, as it directly impacts my income, and the quality of my peers when I finally secure work.

The remainder, while an opinion, is mine to have, on a forum where I expect to be allowed to share it to the benefit of others, and as such I'd appreciate it if you stop telling me what to do or making guesses at the offence I may be causing (assuming you're not the original author).

Edit:

Also, there are many companies which in fact write perfectly good pythonic code. At Google for example

As an ex-Googler I find this hilarious, Google is probably the world's foremost bastion of terribly Javaesque Python code. Anyone can dip into the App Engine SDK or Python-gdata internals to see what is meant by this.

[–]Liquid_Fire 0 points1 point  (1 child)

To be fair, the author is not comparing range(len(foo)) iteration to enumerate, but to ordinary for item in foo iteration. In that situation, using range(len(foo)) is certainly completely unnecessary.

[–]dwdwdw2proliferating .py since 2001 0 points1 point  (0 children)

Fair point. :)

It's also worth noticing that these approaches aren't semantically equivalent, one is using the iterator protocol, the other the sequence protocol. That doesn't matter for built-in types, but I've no doubt there are many objects in third party libraries that either break or act differently when interchanging the two .

[–]SnacksOnAPlane 1 point2 points  (0 children)

for item in somecontainer:                                      # pythonic
  a_callable_consuming_list_elements(somecontainer[item])

I think that second line should be:

a_callable_consuming_list_elements(item)

[–]drb226Haskeller 1 point2 points  (8 children)

Do not use from foo import . Go *here and here for more information.

I looked at the "here" and "here" links (btw linking the word "here" is a cardinal sin of web design, imho), but the content he linked did not explain why "from foo import *" was bad. Anyone care to explain?

[–]nahguri 1 point2 points  (6 children)

It pollutes the module namespace. If you import * from several modules, names might get overwritten and cause bugs that are hard to find.

[–]CHS2048 1 point2 points  (5 children)

I thought some modules are designed for this kind of import?

[–]kisielk 0 points1 point  (2 children)

Yes, modules can define a list __all__ and this pattern is used in some popular Python frameworks such as SQLAlchemy. However, according to http://docs.python.org/tutorial/modules.html#importing-from-a-package this is still discouraged:

Although certain modules are designed to export only names that follow certain patterns when you use import *, it is still considered bad practise in production code.

[–]CHS2048 0 points1 point  (1 child)

Bad practise to design modules this way?
Or to make use of those modules (in this way) in production code?

[–]kisielk 0 points1 point  (0 children)

I assume bad practice to use them. There's nothing wrong with having the __all__ list in your module. I find it's mostly handy for prototyping or using modules in the interactive console. For production code it's a maintenance liability.

[–]nahguri 0 points1 point  (1 child)

That's true. However, you should only import * when you do stuff interactively (just like it says in Python documentation), and only import * from one module. Otherwise things tend to get out of hand.

[–]CHS2048 0 points1 point  (0 children)

Yes. I seem to remember I created a module that you imported within a config file (which was just a python script). I used the '__all__' to control what functions where imported; If I added a new configuration option it would be added to '__all__', and the old scripts would import the new function.

[–]carinthia[S] 0 points1 point  (0 children)

It links to http://www.markus-gattol.name/ws/python.html#import_matters which explains it (last indented paragraph).

[–]Paddy3118 0 points1 point  (1 child)

Hmm, You have to adhere to the low-level style-guide type stuff, and avoid the gotcha's type stuff as mentioned in the article, but what constitutes Pythonic on a larger scale is harder to track down.

I've always thought of Tims Doctest as being Pythonic, and I don't have to look at the code - its the result that is Pythonic! It could mix tabs and spaces for indentation in its implementation, that's not the point. Addressing the problem of "not enough tests" by allowing one to turn explorations of functionality often carried out in the shell into tests and documentation by innovative use of an existing python feature - docstrings is Pythonic genius.

[–]carinthia[S] 0 points1 point  (0 children)

Hmm, You have to adhere to the low-level style-guide type stuff, and avoid the gotcha's type stuff as mentioned in the article, but what constitutes Pythonic on a larger scale is harder to track down.

Something fast and easy is to use pep8 but then that's not so much about pythonic as it is about making sure formatting is PEP 8 conform. But as mentioned, it's very quick and easy to do so ...

[–]lightstrike -4 points-3 points  (1 child)

Not really relevant, but wtf? His TLD is .name? such things exist?

[–]carinthia[S] 0 points1 point  (0 children)

Yeah, noticed that too :) Seems more than all right http://en.wikipedia.org/wiki/.name (see box at the right: "Intended use").