Live data from Hacker News

A few things to remember while coding in Python

satyajit.ranjeev.in

101–110 of 146 posts

Re: A few things to remember while coding in Python

#101

Earlier quoted context omitted.

Actually, if you understand the way Python is evaluated (dig in, the core is pretty transparent), it's the only behavior that makes sense in this case. It's also documented as such[1], so it's quite expected. Default parameters are still just as useful for constants, such as: def f(x=0, y="foo", z=3.14159): This, however, is a perfectly Pythonic idiom: def f(L=None): if L is None: L = [] [1]: http://docs.python.org/r…

I am but an egg, but isn't this the same but shorter? def f(L=None): L = L or []

I originally had "don't do that!" in my comment with your exact code, and edited it out for brevity because I've only seen a couple people do it (and they understood the ramifications, which others have told you). If you're interested in brevity, this is as terse as it gets:

    L = [] if L is None else L

Re: A few things to remember while coding in Python

#102

There is a builtin function called `reversed`. You'd better remember that than the "useful" [::-1] idiom. The recommendation on `iteritems` had better be generalized to include `iterkeys`, `itervalues`, and other opportunities for using iterators rather than building lists. A note that the 'iter...' versions are removed in Python 3 (because iterator behaviour becomes the default) would be appropriate here. In relatio…

>There is a builtin function called `reversed`. You'd better remember that than the "useful" [::-1] idiom.

Reversed produces an iterator and not a copy with all the dangers of mutable semantics. The [::-1] syntax is not equivalent as it returns a copy of the list as if it were reversed, changing up somewhat what it's doing.

You'd better remember that you should know what you're talking about before you start dictating to other people how they should write their code.

Re: A few things to remember while coding in Python

#103

Earlier quoted context omitted.

Also, raising exceptions used to be quite slow, which could hurt for a sparse counting set. Don't know whether that's still the case.

Also Exceptions should be used in "exceptional" circumstances and not as part of normal flow.

One exception (teehee!) to the rule: file operations and other things where atomicity matters. Example code:

  if not os.path.exists("foo"):
      os.mkdir("foo")
That introduces a race condition. If foo does not exist on the first line but is created by something else on the second line then this will raise an exception. The proper code is:

  import errno
  try:
      os.mkdir("foo")
  except OSError as exc:
      if exc.errno != errno.EEXIST:
          raise
That doesn't have the race condition.

(Yes, that's wordy. Eventually they plan to add to Python 3 a fancier exception hierarchy described at http://www.python.org/dev/peps/pep-3151/ , which lets you filter at a more fine-grained level, as below.)

  try:
      os.mkdir("foo")
  except FileExistsError:
      pass

Re: A few things to remember while coding in Python

#104

Earlier quoted context omitted.

The thing is, None is always a possible value for a parameter so it's actually more robust if functions are written to expect None. If you say "f(x=[])" (assuming that worked without the actual side effects it has), someone could still say "x(None)" instead of "x()", causing the function to die. Since a robust program isn't able to avoid checking for None, it might as well set defaults there too. There is another cas…

I disagree. If you want your programs to be robust like that, you now have to check for every case where someone might pass in something stupid (dict instead of int, maybe?). Much better to catch errors further up the chain and keep your low level code simple (ie. pass me something other than an iterable and I blow up). In the expensive case, I'd just calculate it once and store it somewhere (possibly as a lookup dic…

Well that's true, a function is generally written as if it's been given what it wants. I wouldn't check for other types either.

But None is a result that can happen in situations that would otherwise return exactly the expected type. If "nothingness" can be meaningful (especially in a function that accepts an empty list as a parameter, say), it's nicer if the code just deals with None itself instead of requiring checks for None in all the callers.

Re: A few things to remember while coding in Python

#106

Earlier quoted context omitted.

Also Exceptions should be used in "exceptional" circumstances and not as part of normal flow.

One exception (teehee!) to the rule: file operations and other things where atomicity matters. Example code: if not os.path.exists("foo"): os.mkdir("foo") That introduces a race condition. If foo does not exist on the first line but is created by something else on the second line then this will raise an exception. The proper code is: import errno try: os.mkdir("foo") except OSError as exc: if exc.errno != errno.EEXIS…

It's good to point out the race condition.

To solve this particular problem in future code more compactly however, note that Python 3.2 (finally) adds an "exist_ok" Boolean keyword parameter to the multi-directory variant, os.makedirs(). In other words, calling os.makedirs("mydir", exist_ok=True) will silently ignore existing directories and only raise if other errors occur.

Re: A few things to remember while coding in Python

#107

Earlier quoted context omitted.

It appears to me that there is actually no such operator in Python; cf. http://docs.python.org/reference/simple_stmts.html#augmented... Superficially it looks like an operator, but I suspect that's merely because of whitespace freedom; i.e., a, = [0] is equivalent to a,=[0] and a ,= [0].

Sure it is! And in Python 3 there's the ,_*= operator, similar to lisp's car: varname ,_*= [1, 2, 3] # varname == 1

HN isn't the place for facetiousness.

Re: A few things to remember while coding in Python

#108

Earlier quoted context omitted.

Ah. These are the little assumptions that keep blowing off my feet. Thanks.

It is these cases that brought the ternary operator to Python: def f(x=None): x if x is not None else []

Yes, but why? Actually, it's No, because python culture aims to use one way to do thing, the least surprising one. In this case it's:

  def f(x=None): if x is None: x = []

Re: A few things to remember while coding in Python

#109
post #5

It's worth explaining why mutable defaults are bad. The problem with mutable defaults is that they are evaluated once only when the function is defined. Each time the function is called you'll be using the same mutable variable that was created during function definition.

> It's worth explaining why mutable defaults are bad

They can also be good. Here's an example from the Reddit discussion, showing how a mutable default can be used to very neatly and cleanly add memorization to a function:

   def fib(n, m={}):
      if n not in m:
         m[n] = 1 if n 

Re: A few things to remember while coding in Python

#110
post #30

Earlier quoted context omitted.

While it seems logical when you understand what's going on, from a practical point of view I can't see how this would ever be useful. The tradeoff appears to be that the functions are first class objects. I'm not sure what the benefit here is though. Does having them as first class objects allow some useful idioms? (I'm a ruby dev but I'm genuinely curious to know what this allows you to do)

There are a few use cases for default variables on effbot's site: http://effbot.org/zone/default-values.htm Basically, sometimes you do want to reuse the mutable between function calls, and in those cases it can save a fair bit of code passing it in repeatedly.

Good coverage. I use it quite often for cache dictionary, it's much simpler API and overall code, than creatng a new class for it. Demo snippet from effbot's site:

  def calculate(a, b, c, memo={}):
    try:
      value = memo[a, b, c] # return already calculated value
    except KeyError:
      value = heavy_calculation(a, b, c)
      memo[a, b, c] = value # update the memo dictionary
      return value
Post reply on HN