Hacker Newsnew | comments | show | ask | jobs | submit login
Python Idioms [pdf] (safehammad.com)
237 points by benn_88 582 days ago | 124 comments



Interesting philosophical points.

To me personally, testing for 'truthy' and 'falsy' values, or relying on exceptions rather than checking values in advance, feels like sloppy and imprecise programming.

A string being empty or not, or an array having items or not, or a boolean being true or false, are all qualitatively totally different things to me -- and just because Python can treat them the same, doesn't mean a programmer should take advantage of that fact. Sometimes it's possible to over-simplify things in a way that obfuscates instead of clarifying.

When I read:

    if name and pets and owners
I have no intuitive idea of what that means, of what's going on in the program. When I read

    if name != '' and len(pets) > 0 and owners != {}
I understand it exactly.

But by this point, I've come to understand that, for a lot of people, it seems to be the opposite. It seems to be more of a philosophical difference, not right/wrong.

-----


It's a dynamic-typing thing. In some hypothetical static-strong-non-duck version of Python,

  if name != '' and len(pets) > 0 and owners != {}
would tell you that name is a non-empty string, pets has values, and owners is a non-empty dict (except it doesn't work, as pdonis noticed).

But Python allows immoral implicit conversions, so that's not what that line means! If name is a function, pets is the value '7' and owners is a list, the test passes.

  if name and pets and owners
Would pass as well, but it has the advantage of not implying it does more than it does: all you can infer from the test passing is that none of name,pets,owners are special falsy values.

If you actually wanted to test what the longer line is implying, you'd write something like

  if isinstance(name, str) and isinstance(pets, list) and isinstance(owners, set) and name and pets and owners
(don't do this, it violates duck-typing and LBYL)

-----


owners is a non-empty set.

No, it doesn't tell you that. {} denotes an empty dict, not an empty set; and an empty set will return True for owners != {}, not False. As I noted in another post upthread, you would need to write

    len(owners) > 0
to get the correct semantics, making owners indistinguishable from pets even if they are different container types. If you really wanted to make all the types clear, you would need to include the isinstance tests.

-----


You're right, edited.

-----


Sure, if you view the code as a conversation between the reader and the compiler, but as a conversation between the reader and the writer, I would hope that those inferences (e.g. name is a non-empty string) would be reasonable.

-----


> it's possible to over-simplify things in a way that obfuscates instead of clarifying

While this is true, I think it's important to keep in mind that a big benefit of being able to handle a situation programatically in different ways is that you, as a programmer, are more able to accurately describe intent.

For example, I think it's foolish to always catch exceptions instead of checking beforehand (look-before-you-leap). However, being able to do either allows me to more accurately represent the intent of a statement. Blindly following rules is almost always sure to lead to poor decisions.

If I'm retrieving an element from an array and I know that for the majority of possible application states there will be an element there, I can just access it, and handle an exception (because it's an "exceptional" case). However if I'm expecting that it could go either way in typical circumstances, then I might more explicitly check the index in the array to illustrate my intent that yes, there are two distinct paths here and that is how the program's control flows naturally.

Of course all of these things are only worthwhile if the reader can see and interpret them as purposeful decisions rather than coincidence. Conveying that is something else entirely.

-----


I think the point of the article, and of idioms in general, is that they make code "better" (e.g. some combination of clearer, shorter, cleaner, etc.) for the community of coders who are familiar with the idioms. The obvious downside of idioms is that a programmer needs to learn the idioms to reap these advantages. So I suppose whether you should encourage idioms in your code base would depend on who will be working on the code base. I suspect most traditional software companies (even startups) will employ programmers who will either know the idioms of the language being used, or will be willing and able to learn them.

-----


Nonetheless, you should reconsider from time whether those idioms are actually helpful for those in the community. I personally dislike 'if name and pets and owners:' because it removes the information of what is happening at this line of code and I have to look up what type name, pets and owners actually are.

-----


it removes the information of what is happening at this line of code and I have to look up what type name, pets and owners actually are

My response to this is, why do you care what their types are? What the statement is saying, conceptually, is "if there's a name and there's pets and there's owners, do this". Why should I have to explicitly tell the language how to tell whether there's a name and there's pets and there's owners, when it already knows that? To me that's just extra verbosity for no good reason. Do you really care that name is a string but pets and owners are containers? (And even if you do, isn't that evident from the variable names anyway? Good naming conventions can do a lot of the work of clarifying what's going on while keeping the code compact.)

-----


That's a good point. In the example they give it's not really an issue, since the variables are clearly defined right before the if statement. I still think it's reasonable and intuitive in most cases, once you're familiar with the "truthiness" of basic data types (e.g. empty arrays, empty strings, empty dicts, and numbers equal to zero are "falsy").

-----


>> It seems to be more of a philosophical difference, not right/wrong.

Exactly. It's philosophically different from other languages, but it's standard in Python.

-----


I think part of the reasoning behind the truthy / falsy mechanic is that it's more robust. If, for whatever reason, we did:

  name = None
instead of

  name = ''
Then the second conditional would fail, whereas the first would still be fine.

-----


I've personally run into problems when I don't do exact comparisons with True and False. For example, I've forgotten to return a value in one path of a function/method, and then tried to use the result in an if statement in the style recommended by the OP (e.g. if fcall(): do something). After being bitten several times by this, I always do explicit comparisons.

-----


So you never use "else"? Else is the mother of inexact comparisons.

-----


In this particular situation, there was no else. I probably added an else clause with an assert return_value == False, as the function call was a virtual dispatch that could have many implementations. Of course, I wouldn't do that for every if/then/else statement in my code. In general, I'd prefer a stricter language that only permitted a boolean as a condition in the IF statement, avoiding this problem altogether.

If you are using truthy/falsey values, I think it can be a code smell that you are not doing enough to catch invalid values up front or should normalize the values closer to their creation point.

-----


Use the else to let you know that something is going wrong, using raise or exit() or some die() function:

    if something:
        do_this()
    elif something_else:
        do_that()
    else:
        # hope we don't end up here
        raise UserWarning('We shouldn't have ended up here')

-----


But the discussion is about truthy values and inexact comparisons - avoiding inexact comparisons and having an else means:

    if something==True:
        do_this()
    elif something==False:
        do_that()
    else:
        raise UserWarning("Shouldn't be here")
Which is a code smell to me. What I would do is:

    assert isinstance(something, boolean), "Shouldn't happen"
    do_this() if something else do_that()
(replace assert with something else if you want it not to be optimized away with -O; assert is a debug-only construct in Python. Or just drop the assert altogether. In most places, I would - there's no end to the amount of validation you could do, and most of it is unnecessary)

-----


Checking for empty strings can be done with len(mystring)==0 for this reason. In many other languages this method is standard and recommended practice.

Relying on implicit conversions is just sloppy. What if that variable was never supposed to be None in the first place. Better with an exception than continuing with corrupt data.

Remember another python motto: Explicit is better than implicit.

-----


> Checking for empty strings can be done with len(mystring)==0 for this reason.

`len` blows up on None, so this blows up completely instead of just failing.

> Relying on implicit conversions is just sloppy.

There is no implicit conversion. Truthiness is a protocol, it does not convert anything anywhere.

-----


> `len` blows up on None, so this blows up completely instead of just failing

That's the whole point! As I said, Better with an exception than continuing with corrupt data. Or maybe I should say unsupported data type rather than corrupt data.

-----


At the point where you are doing your validation (or where you are doing the actual work with the variable) you can make that decision.

It's entirely possible that upstream code is content with any type as long as it's coercible. A lot of Python code just wants an iterable, for example, or something that can be coerced to a string.

It's definitely possible to have a situation where you need a string specifically (or an integer specifically), but it's generally better to have your code coerce the data whenever possible so as to allow for more logical code.

e.g. 's.send(str(object))' (if object has a relevant __str__()) makes perfect sense in code. You don't necessarily need a string, you just need something that can behave as a string in a sensible manner.

You also end up with scenarios where None is a 'valid value' in e.g. the SQL sense of 'value not specified'; for example, company_name might be '' (empty string provided) or None (no value provided); either way company_name is Falsey, and your logic should work the same.

-----


If you need to differentiate between the empty string and None, you can, but in most situations the difference isn't important. E.g., would you ever actually display the values differently? Exaggerated explicitness can be misleading. E.g., if a year from now someone is making some changes, should they really be distracted by the fact that you've used 3 different if/else cases for the same semantic result?

-----


See my response as well, but it depends on your intent.

None and '' are two completely different things, and could mean different thing sin the context of the application. It's quite likely that choosing either is a conscious choice of the programmer.

-----


The computer sees None and '' as different. But the users of my database at work just see a blank entry. As far as they are concerned, they are the same. For that reason it can be useful to have the code treat them the same.

-----


In this particular case, you want to write

  name is None
since None is a unique distinct value

edit: didn't notice at first that you wrote an assignment, not a comparison

-----


Note that 'is' in Python is kind of tricky. None is None, and any value==None is also None. 1 is 1, and 100 is 100; 256 is 256 but 257 is not 257, and "test" is not "test".

The reason for this is that 'is' tests to see if both operands are the same object; it's not a test of equivalence on any level.

There is only ever one instance of None, and you simply get or create references to it. Likewise, there is only one instance of integers up to 256 (presumably a performance optimization for small loops, indexes, etc.). Integers after 256 get separate instances each time, so 'is' fails after that point.

A lot of newcomers to Python come across the 'is None' construct and make assumptions about what 'is' does; the worst part is that they're often correct, and the behaviour becomes difficult to track down when it changes.

-----


I understand it exactly.

Really? Containers of different types have a len method; which type of container is pets? The line you wrote doesn't tell you. And is owners supposed to be a set? If so, your comparison to an empty dict will give the wrong semantics if owners is an empty set (owners != {} will return True for an empty set, not False). You would have to write

   len(owners) > 0
to get the correct semantics. Which, of course, already obscures the type of owners, just as the type of pets is obscured.

In short, your suggested "improvement" over idiomatic Python still obfuscates rather than clarifying.

-----


No. What if 'owners' is a collections.defaultdict, or some other dict-like type?

-----


>When I read:

> if name and pets and owners

>I have no intuitive idea of what that means, of what's going on in the program.

I agree with you, when I read that I think if name and pets and owners, what? All equal each other? Seems like an unfinished statement.

-----


Out of interest what language do you use? Coming from Perl, this seems pretty normal to me.

-----


but what if owners is something that looks like a dict but is not? same for name.

-----


I disagree with promoting try / catch. Exceptions like ValueError can really happen almost anywhere, so it is usually better to sanitize your inputs.

E.g. something like:

    try: 
        something = myfunc(d['x'])
    except ValueError:
        something = None
The programmer's intent is probably to only catch errors in the key lookup d['x'], but if there is some bug in the implementation of myfunc() or any of the functions called by myfunc() which causes a ValueError to unintentionally be raised, it will be caught by the except.

For dictionary lookups specifically, get() is usually preferable:

    something = d.get('x')
    if something is not None:
        something = myfunc(something)
Or if dictionary may potentially contain None values:

    if 'x' in d:
        something = myfunc(d['x'])

-----


Not to counter your point, but I would like to quote from PEP8 here:

Additionally, for all try/except clauses, limit the try clause to the absolute minimum amount of code necessary. Again, this avoids masking bugs.

Yes:

    try:
        value = collection[key]
    except KeyError:
        return key_not_found(key)
    else:
        return handle_value(value)
No:

    try:
        # Too broad!
        return handle_value(collection[key])
    except KeyError:
        # Will also catch KeyError raised by handle_value()
        return key_not_found(key)

-----


These two comments together make a lot of sense.

When your try block is a single lookup, you might as well use an if statement or get. However, when the 'absolute minimum' is nontrivial try/except is still a good option, e.g.

  try:
      name = employee['name']
      first_name = name['first_name']
      last_name = name['last_name']
  except KeyError:
      print "Bad employee data"
      return

-----


Also note the second argument to the get method specifies the default value to return in case of a KeyError:

    something = d.get('x', default)

-----


While not disagreeing with your case, there are some cases where I try to follow this rule because in most of those cases, I would expect that there is no exception and if there is, then catch it.

  try:
      open(FILE)
  except IOError:
      it failed

-----


That example would surely be better as:

    something = myfunc(d['x']) if 'x' in d else None

-----


In this form you perform the lookup twice: once to test 'x' in d and then again to actually get the value d['x']. Try clauses in Python are very inexpensive if they pass (don't raise an exception), so often the try..except version would be preferable.

In any event don't optimize prematurely and use a profiler rather than guessing if performance is an issue. ;-)

-----


Doesn't this go against the Pythonic notion of handling exceptions rather than "looking before you leap"?

-----


That looks like perl.

-----


what's going to happen in your second example if somebody gives you a defaultdict instead of a regular dict for d.

-----


For point 10:

'_' is often aliased as gettext to ease translation of string:

    from django.utils.translation import ugettext as _

    translated_str = _('Something to translate')
so using it will overwrite the alias. Instead, you can use '__' (double underscore) as ncoghlan suggests below his answer [1]. or you can use the 'unused_' prefix as Google Python Style Guide suggests [2] or you can change your code, so you don't need to use '_' as Alex Martelli suggests in his answer [3].

[1]: http://stackoverflow.com/a/5893946/720077

[2]: http://google-styleguide.googlecode.com/svn/trunk/pyguide.ht...

[3]: http://stackoverflow.com/a/1739541/720077

-----


I have personally never seen this before. I'd be wary to use it, since it breaks the "_ is a throwaway" idiom, as well as the REPL "_ is the results of the last expression" function.

Aliasing it to "t" or "txl" seems like a saner way, if I'm honest.

-----


you never use gettext in REPL :) and it's common to use '_' as gettext. Less typing, more readable strings. If you don't believe me, you should believe Alex Martelli [1], a random Python developer [2] or just read Official Python documentation [3] which recommends assignign gettext.gettext to '_'...

[1]: http://stackoverflow.com/a/2745537/720077

http://stackoverflow.com/a/1739541/720077

[2]: http://stackoverflow.com/a/5893946/720077

[3]: http://docs.python.org/2/library/gettext.html

-----


This has been established practise in Zope, Plone et al for many years.

http://developer.plone.org/i18n/internationalisation.html#ma...

-----


It's standard Python practice since Martin von Löwis's work in support for internationalization, which he presented at the IPC6 Python conference in 1997.

Quoting from http://www.python.org/workshops/1997-10/proceedings/loewis.h... :

> Here are several ways of producing the message catalogs. This is the one suggested by the GNU gettext documentation. First, all translatable message strings in the source code must be marked. In order to disturb readability as little as possible, the wrapper function around each string should be called _ (underscore). This use of the underscore usually does not interfere with its meaning in the interactive mode. A gettextized module would then begin with

    import intl
    
    _=intl.gettext

-----


I got caught out by this recently. I wasn't expecting it to be used as a method alias and assumed it was an unusual part of Pythons syntax that I hadn't discovered. I had to get the answer from Stack Overflow.

Not very intuitive.

-----


One of the handy things not mentioned:

   with open("x.txt") as f:
       data = f.read()
       # do something with data

-----


absolutely! that one is quite high up on my python_idioms_to_import file

-----


The very first one, "Make a script both importable and executable," needs some caveats. It's useful sometimes, but people often use it in places where it is not a great idea. Here's why:

1) If you are in the mindset of "I want a single file which is both a library and a program," how will you name it? Files to import must end with ".py" and follow C-style name rules, so cannot start with a number cannot contain hyphens. This leads many people to break conventions when naming their programs, because on Unix, hyphens are conventional in program names but underscores are not (count the examples of each in /usr/bin on any system). And naming your program something like "2to3" is impractical if you want it to be importable also.

2) It is unclear where to install files which are both programs and libraries. Programs usually go in some sort of "bin" directory (again, on Unix systems), but libraries do not. Where do you put a file which is both?

3) Sometimes the __name__ == '__main__' trick is used to include unit tests within a module. That's not bad, but consider using Python's excellent doctest feature instead, which often serves the same need but in a richer way.

-----


I use the __name__ == '__main__' thing for unit testing.

I don't know if this is a generally applicable technique, but a lot of my modules interact with hardware or physical measurements, so I have to "see" the results in order to believe that the units are working. Often, what I'm looking for is problems with what's actually being measured, and the effect of changing operating conditions, not just my own copious programming bugs.

For this reason, my unit tests can be pretty elaborate, with GUI, graphs, and other stuff. The unit test also functions as a "demo" of the module.

-----


Absolutely--that's a great example of a library module that may also be usefully executed. I had something similar today: a module that sends email. It's useful to be able to run it (by explicit "python foo.py", not chmod +x) and see a sample email in my inbox.

Unfortunately, for every good use of this trick, there seem to be two poor ones. Oh well. Most of the things in TFA are more generally applicable.

-----


> 9. Create dict from keys and values using zip

In 2.7+, I'd recommend a dictionary comprehension instead.

-----


In this case,

    {k: v for k, v in zip(keys, values)}
edit:

As I mention below, this becomes useful when you want to do e.g.

    {f(k): g(v) for k, v in zip(keys, values)}

-----


I'd disagree, as

  dict(zip(keys, value))
is more concise, doesn't introduce extra variables, and doesn't repeat itself, and explicitly names a dict rather than using a symbol.

-----


I like list comprehensions and to me dict comprehensions feel like a natural extension of this. It means that you can do things like

    {k.upper(): v ** 2 for k, v in zip(keys, values)}
Or generally

    {f(k): g(v) for k, v in zip(keys, values)}
I find this very extensible. In terms of conciseness, they both fit on a single line and I like the whitespace inside a dict comprehension.

-----


Right, but the second you need to modify either the key or the value, you are better off with the dict comprehension. It's like map() vs [...].

-----


"Simple is better than complex."

-----


As an implementor of Hy (a homoiconic lisp frontend to Python) I've found certain Python idioms to be rather infuriating of late.

In particular:

    >>> 0 == False
    True
Which makes the idiom of testing truthiness quite annoying in parsing code such as:

    def is_digit_char(s):
        """ Return a parsed integer from 's'."""
        try:
            return int(s)
        except (ValueError, TypeError):
            return None
Which is harmless enough except that as a predicate it sucks because parsing "0" will return False in a context where I'd rather know whether I parsed an integer or not... which leads to non-idiomatic code.

This is mainly because True/False are essentially aliases for 1/0 and as such won't identify so:

    >>> 0 is False
    False
    >>> 0 is 0
    True
So it's important to remember another Tim Peters-ism: Although practicality beats purity. As read in the Zen of Python it seems he's referring to special cases which this might be.

As a shameless aside, you should see what we're working on in Hy. There will likely come a point where we'll be able to do kibit-style suggestions of idiomatic transformations to your Python code.

Update: I ran into this while trying to write token parsers for a parser-combinator lib in Hy.

-----


Your function is_digit_char(s) is peculiar. From the name I would expect it to return a boolean, instead it returns a number or None.

To write it like that and have a problem with the falseness of 0 it means that you use it in a way to both use it as conditional expression and an integer value, e.g.

    digit = is_digit_char('0')
    if digit: # fail
        print(digit)
     else:
        print('not a digit')
You should then check for his equality to None

     digit = is_digit_char('0')
     if digit is None: # pass
         print('not a number')
      else:
         print(digit)
But I would argue that you were in search for troubles when you wrote an is_something() function that doesn't return a boolean. That is not idiomatic.

p.s. Hy is too crazy :-)

-----


It's a trivial example and I wouldn't focus on it too much.

It's not that peculiar -- instead of parsing the same character twice you simply return the value that you parsed or None. My inspiration was from the CLHS predicate function, DIGIT-CHAR-P [0].

The real point I was making is that Python has warts that make writing idiomatic code impractical in some situations. I suggest that practicality take precedence over purity. There are some situations that lead to non-idiomatic code and that's okay.

[0] http://clhs.lisp.se/Body/f_digi_1.htm

Update: forgot the link. :)

Update update: Perhaps peculiar to Python because all values of integers are not False except for 0 whereas in another language that doesn't have this wart, anything that isn't False is True... even 0. In other words, anything that isn't False is True. :D

-----


> In other words, anything that isn't False is True. :D

Except for `nil` (lisps, ruby), and possibly a host of other things depending on the language (empty strings in javascript).

-----


Scheme is the only PL I know that has an explicit #f value.

CL, for all intents and purposes, treats nil as False... but some find the conflation of nil and the empty list runs into the same issue when operating on s-exprs.

    CL-USER> (if '() 1 0)
    0
vs

    scheme> (if '() 1 0)
    1

-----


What is a "wart" is subjective. C-like languages also have this "wart" and it's used to great effect, enabling different idioms that would be messier without it.

-----


Logged in to post basically the same comment, then saw you already had. Essentially, no one who knows what they're doing in Python would write the function that way.

-----


It's a trivial example. I've been writing Python since 2.3... maybe I should stop.

-----


I'm surprised by your definition for a function named is_digit_char. I'd expect such a function to be something like:

    return len(s) == 1 and s[0] in string.digits
Or

    return re.search('^\d$', s)
Then it can be used idiomatically. The function you defined I'd call parse_int:

   def parse_int(s)
     "Return a parsed integer from 's' or None if it's not an int."""
      ...
Which would then be used as:

   i = parse_int(s)
   if i is None:
     ...your function definition...
$0.02.

-----


I might try this... it wasn't actually named is_digit_char; I was actually writing a token parser for a "monadic" parser combinator library in Hy and my function was more idiomatically named there, integer-char? (after the CLHS function DIGIT-CHAR-P).

-----


This reminds me, somewhat tangentially, of a bug I ran into once with Tcl (yes, Tcl) where a function returned an integer in the form "08" and Tcl had a convention that any integer with a 0 prepended would be treated as octal in any arithmetic expressions. Naturally, 8 is not a valid octal digit, so this broke everything when it happened.

Ultimately it was my fault for not being better acquainted with the language, but that one still caught me by surprise.

-----


Well, it's not really Python's behaviour, just the behaviour of that type. You can always use your own type:

  class I(int):
    def __nonzero__(self):
      return True

  def is_digit_char(s):
    try:
      return I(s)
    except (ValueError, TypeError):
      return None

  >>> if is_digit_char('0'): print "True"
  True

-----


Why not use the Python built-in method "anystring".isdigit() ?

-----


anystring in string.digits

-----


Although many of them boil down to preferences and philosophical points of view, I find these kinds of idioms useful. Whenever I write code in a new language, I want to "write as a native" so that I can maximize the effect that the language has on how I think about programming.

For Python in particular, Jeff Knupp's "Writing Idiomatic Python"[1] (not free, but not expensive, either) goes into detail on a lot of the concepts in the OP's slides. (I'm not affiliated with Jeff in any way, just a satisfied customer.)

[1] http://www.jeffknupp.com/writing-idiomatic-python-ebook

-----


What level would you say the idioms in the book are at? I have already been programming in python for a little while, and I wouldn't want to pay for something which I already know. It would be nice if there were more sample idioms on that site so you could have a better idea of what the rest of the book was like.

-----


Given that the author is willing to give away copies to those that can't afford the book, I'm sure he'll consider a preview/promise refund if you send him an email? (See bottom of page).

Note: Also not affiliated in any way.

[edit: given:

http://www.goodreads.com/book/show/17354838-writing-idiomati...

it doesn't look like it's of all that much value to an experienced python developer. I'd love to hear some other comments, though.]

-----


It's of more use to a new Python developer than an old hand, but it does touch on some relatively unknown language features (eg for loops having an else clause). Honestly, though, it's only $10, so it's not a massive investment.

-----


Alex Martelli gave a nice talk called "Permission or Forgiveness" about the exception handling style recommended by OP: http://pyvideo.org/video/1338/permission-or-forgiveness-0. He has some nice insights into this issue.

That being said, I think there are some situations where you want to check for problems up front (possibly in addition to exception handling). In particular, if you are parsing some data from outside the program, you may want to provide some context about what was wrong. KeyError is not very helpful to your users.

-----


As a huge Python fan, I'm ashamed to admit but I don't get the

    while True:
        break
What's the problem? I supose the use case is

    while True:
        # do stuff
        if some_condition:
            break
What is the alternative? 'while some_condition'? That means we must have the 'some_condition' variable outside of the loop. And if we have multiple exit points it may become a mess.

-----


I think this is more for:

   while True:
     result = do_something()
     if not result: 
        break
rather than:

   result = True
   while result:
      result = do_something()

[[edit apparently tab ret submits, not whatever I was trying to do with the actual editing]]

-----


I prefer the second one and its a line less code.

-----


That means we must have the 'some_condition' variable outside of the loop.

I'm not sure what you mean by that, this works just fine. Though you would probably use range in the real world.

    i=0
    while i !=5:
        i+=1
Instead of

    i=0
    while True:
        i+=1
        if i == 5:
            break

-----


He means the variable used in the condition to break (or start/not start), i.e. "i" in your example - which you have outside of your loop.

-----


OK, that makes sense then. I was thinking "the condition" was the variable changing within the loop.

-----


Personally, because I find infinite loops to be a real PITA, I prefer to do:

    for _ in xrange(100000):
        break
    else:
        logging.error("ran into an infinite loop")
unless I really do need an infinite loop for things like event handler loop, which is admittedly quite rare.

-----


Doesn't it bother you that this code is technically completely wrong?

-----


That seems incredibly silly, and looks like it could lead to very infrequent bugs (the worst kind). `while True` is shorter, simpler, and conveys the actual purpose better.

Maybe having a statement like that when testing is okay, but in production code that looks insane.

-----


I'd argue that `while True` would be fine in testing, but problematic for production, particularly in the types of programs I write most frequently (long running, minimal supervision). In these daemons, stalling on infinite loops is significantly more painful than dropping out of a loop early occasionally (and with proper logging that it did fall out of the loop, to boot).

It's certainly not as idiomatic, but it's more correct in the long run. My eyes were opened to this when reading through the NASA C guidelines. Closing the door on infinite loops lets programs recover gracefully and do the correct thing for the duration of the programming, as opposed to thing that may be correct for that moment of operation: i.e. looping 1-2 more times beyond the limits of the xrange counter.

-----


Also an easy way to fake a do ... while loop.

-----


If there is a simple condition I prefer "while condition" because it makes the code easier to read: I can tell (roughly) when the loop is supposed to exit without reading the contents of the loop and scanning for a break.

-----


I would add generator expressions:

    (f(x) for x in list_of_inputs)
Just like a list comprehension, but with (...) rather than [...] and with lazy evaluation.

These are useful when you don't need to evaluate all of the inputs at once but still want to iterate over them at some point later on.

-----


Not to mention how awesome they become when you feed them as a set of values to a function.

    values = (f(x) for x in list)
    g(*values)

-----


Thanks for this write up. I didn't know about enumerate. I never thought of swapping variables as in example 4 either.

I noticed one small mistake in section 9:

  d[keys] = values[i] 
Should be:

  d[key] = values[i]

-----


For a good few months I was doing

    for i in range(len(input_list)):
        v = input_list[i]
        ...do something with v and i...
enumerate is much nicer!

    for i, v in enumerate(input_list):
        ...

-----


This is a great, if somewhat basic list. My $0.02: Python is a tool for thinking clearly. That you can run your "thoughts" as written is a very nice bonus, of course.

There are some really interesting things that Python allows:

    >>> d = {}
    >>> d[23], d[14], d['hmm'] = 'abc'
    >>> d
    {'hmm': 'c', 14: 'b', 23: 'a'}

-----


I couldn't resist. Moar funky-cool Python:

    >>> from string import ascii_letters
    >>> ors = ['|'] * (len(ascii_letters) * 2 - 1)
    >>> ors[::2] = ascii_letters
    >>> ''.join(ors)
    'a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z'
(Obviously, in this example '|'.join(ascii_letters) would suffice, but if the objects weren't strings...)

-----


Not gonna lie... your snippet made me say "WTF?" Why not just do:

  from string import ascii_letters
  '|'.join( ascii_letters )
(I also like list slicing... but only when necessary.)

-----


Yeah, I realized after posting it. (See edit.) The original code was using non-string objects. I basically come up with this to get the same behavior as str.join(). ;-)

-----


    '|'.join(str(x) for x in y) # or
    '|'.join(map(str, y))

-----


The original use for that code was to build a list of parsing objects to create a single parsing object that was the OR'ing of the basic ones.

Aw, heck, code speaks louder than words:

https://github.com/PhoenixBureau/PigeonComputer/blob/master/...

-----


    intersperseM or $ map chartok whitespace
Oh... wait... sorry... wrong language.

-----


His last slide could be written more idiomatically as

    ''.join('Thanks!')

-----


And this would be more idiomatic:

    'Thanks!'
(It is faster also! :D)

-----


Ahh but that is a mere expression, not an idiom :-)

-----


Ah yes, just like the

  set('abcd')
idiom.

-----


Srsly, what Python programmer writes the code in the "Bad" examples therein? This list looks like it's from 2005 or something.

-----


Lists like this aren't for experienced pythonistas, but more for new people to know what things to avoid, and what not to copy if they do come across it online (in an article from 2005...) :-)

-----


On page 20, there is slight mistake - count is used in the second ("NOT SO GOOD") example but "i" is printed.

-----


Or was it really a mistake?

-----


Are there any anaphoric variable names in python, like cmd.exe?

-----


On page 20, didn't he/she mean "print(count, name)" in the BAD example?

On page 18, the Java comment: it depends. E.g. see [1]

[1] http://stackoverflow.com/questions/299068/how-slow-are-java-...

-----


>pets = ['Dog', 'Cat', 'Hamster']

>for pet in pets:

> print('A', pet, 'can be very cute!')

This may be nit picking but I prefer output like this:

print 'A %s can be very cute!' %(pet)

-----


I prefer

  print("A {0} can be very cute!".format(pet))
.format() is very versatile.

  d = {'first': 'Robert', 'last': 'Paulson'}
  print("His name was {first} {last}!".format(**d))
  >> His name was Robert Paulson!


  class Person:
      def __init__(self, first, last):
          self.first, self.last = first, last

  p = Person('Robert', 'Paulson')
  print("His name is {0.first} {0.last}!".format(p))
  >> His name was Robert Paulson!
You also do not need to know what type is being passed in as the __str__ method is used for .format().

-----


Possibly because you haven't moved to Python 3?

-----


Python 3 doesn't dictate that style, just the parentheses

-----


Another one that I find useful -- using `map` instead of list comprehensions when you want to apply a function to every element. So instead of:

    [str(x) for x in array]
Do this:

     map(str, array)

-----


I don't understand the truth table on slide 9 for " - vs None " and "__nonzero__ (2.x) " __bool__ (3.x) vs __nonzero__ (2.x) " __bool__ (3.x) "

-----


I'm pretty sure the "-" vs "None" meant that there wasn't a truthy alternative to None. More accurate would have been "N/A" vs "None". Not clear on the __nonzero__/__bool__ stuff...

-----


__nonzero__/__bool__ are the methods called on objects when evaluating truthiness. So, if you're implementing custom objects, those are what you would use, and return either True or False, based on their truthiness value.

As an example, you might represent __bool__ on a database connection object to reflect whether there is a live connection, allowing you to:

    if not conn:
        conn = MySQLdb.Connect()

-----


He's referring to the special methods available to classes that allow implementation of the truth value of an object. In version 2.x of Python, this method is __nonzero__[1] and in version 3.x it's called __bool__[2]

[1] http://docs.python.org/2/reference/datamodel.html#object.__n...

[2] http://docs.python.org/3/reference/datamodel.html#object.__b...

-----


String concatenation is pretty fast nowadays with the + operator.

-----


Do you have a source? Also, I would argue that concatenation with + is less readable than string formatting.

-----


I think that concatenation with + is okay when you're concatenating two strings. And CPython can optimize x = x + y or x += y calls.

http://docs.python.org/2/library/stdtypes.html

-----


I'd love to see a writeup like this for Perl or Javascript. Has anyone come across one?

How about Java?

-----


i agree with all but #2. this seems to embrace conciseness as simplicity or understandability. it forgets the more cardinal value that explicit is better than implicit.

-----




Applications are open for YC Winter 2016

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | DMCA | Apply to YC | Contact

Search: