views:

235

answers:

7

Option 1:

def f1(c):
  d = {
    "USA": "N.Y.",
    "China": "Shanghai"
  }

  if c in d:
    return d[c]

  return "N/A"

Option 2:

def f2(c):
  d = {
    "USA": "N.Y.",
    "China": "Shanghai"
  }

  try:
    return d[c]
  except:
    return "N/A"

So that I can then call:

for c in ("China", "Japan"):
  for f in (f1, f2):
    print "%s => %s" % (c, f(c))

The options are to either determine whether the key is in directory before hand (f1), or just fallback to the exception (f2). Which one is preferred? Why?

+6  A: 

Typically, exceptions carry some overhead, and are meant for truly 'exceptional' cases. In this case, this sounds like a normal part of the execution, not an 'exceptional' or 'error' state.

In general, I think your code will benefit by using the "if/else" convention, and saving exceptions for only when they are truly needed.

Jweede
which remind me "code as you think", "things do better as they designed for". so i mark it as the answer.
Dyno Fu
You might be correct when you say Exceptions come with an overhead, but the python style is `Its better to ask for forgiveness than permission`. That means you dont check if something's there or not. You `try` and do it and later ask for forgiveness. If it were me, I'd have gone with a `try/except`
jeffjose
That's a good point jeffjose. It's smart in python to try things out and see what works. However using a try/except clause in this case when we don't expect anything exceptional is not a good idea. Like Richo said: "bad karma". :-)
Jweede
+18  A: 

Neither, I would go for

def f2(c):
  d = {
    "USA": "N.Y.",
    "China": "Shanghai"
  }

  return d.get(c, "N/A")

This way is shorter and "get" is designed for the job.

Also an except without an explicit exception is bad pratice, so use except KeyError: not just except.

Exceptions do not have much overhead in python. It is generally better to use them if there are not better alternatives or sometimes even when it saves an attribute lookup (to use instead of hasattr).

Edit: to clear up general point about exceptions.

paxdiablo is correct on the general point. Python is mainly designed for "its easier to ask forgivness then permission" i.e try then see what fails (exceptions), then "look before you leap" see whats there and then apply. This is because attribute lookup in python can be expensive, so calling the same stuff again (to check boundries) is a waste of resources. However, internal stuff in python has normally got nicer helpers so its better to use them.

David Raznick
the question is not specific for dict, it is mainly for exhibit. what i want to know is to check the boundaries before hand or retreat to exception.
Dyno Fu
@Dyno: then you should have asked *that*. A good rule of thumb is "better to ask forgiveness than permission", but I will add that if the common case is going to result in asking for forgiveness a lot, then you should ask forgiveness first.
Alok
do you mean "just do it". if it goes wrong, then deal with it later?
Dyno Fu
I would say "just do it", BUT make sure you know what will go wrong. Dont catch everything, only particular exceptions you are happy to happen. If you catch everything, when something serious goes wrong it will be hard to find as you will get no feedback.
David Raznick
+7  A: 

Neither.

return d.get(c, 'N/A')
Ignacio Vazquez-Abrams
+8  A: 

In general terms (not necessarily Python), I tend to prefer the "try-then-tell-me-if-it-went-wrong" method (exceptions) in all but the simplest cases. This is because, in threaded environments or during database access, the underlying data can change between the key check and the value extraction.

If you're not changing the associative array outside of the current thread, then you can do the "check-first-then-extract" method.

But that's for the general case. Here, specifically, you can use the get method which allows you to specify a default if the key doesn't exist:

return d.get (c, "N/A")

I'll clarify what I stated in the first paragraph. In situations where the underlying data can change between checking and using, you should always use an exception-type operation (unless you have an operation that will not cause a problem, such as d.get(), mentioned above). Consider for example the following two thread time-lines:

+------------------------------+--------------------+
| Thread1                      | Thread2            |
+------------------------------+--------------------+
| Check is NY exists as a key. |                    |
|                              | Delete NY key/val. |
| Extract value for NY.        |                    |
+------------------------------+--------------------+

When thread 1 attempts to extract the value, it will get an exception anyway, so you may as well just code for the possibility and remove the initial check.

The comment about databases is also relevant since that's another situation where the underlying data can change. That's why I tend to prefer atomic SQL (where possible) rather than something like getting a list of keys then processing them with individual statements.

paxdiablo
I always called them. Its easier to ask forgiveness then permission" and "look before you leap" respectively. But I agree in python forgiveness is what you are after.
David Raznick
i tend to mark both this one and jweede's as the answer, but seems this is not doable.
Dyno Fu
+4  A: 

I'm with David on this one:

def f2(c):
   d = {
        "USA": "N.Y.",
        "China": "Shanghai"
       }

   return d.get(c, "N/A")

...is exactly how I'd write it.

To address your other options:

In 'f1()', there's nothing wrong with this, per se, but dictionaries have a get() method for pretty much this exact use case: "get this from the dict, and if it's not there, return this other thing instead". That's all your code is saying, using get() is just more concise.

In 'f2()', using 'except' by itself like that is frowned upon, and in addition, you don't really do anything useful in response to the exception -- in your case the calling code will never know there was an exception. So why use the construct if it doesn't add value to your function or the code that calls it?

jonesy
+1  A: 

I see people using "get", which I recommend. However, if you find yourself in a similar situation in the future, catch the exception you mean:

try:
    return d[k]
except KeyError:
    return "N/A"

This way, other exceptions (including KeyboardInterrupt) don't get caught. You almost never want to catch KeyboardInterrupt.

Dietrich Epp
A: 

I agree that in this case, dict.get is the best solution.

In general, I think your choice will depend on how likely the exceptions are. If you expect the key lookups to mostly pass, then the try/catch is a better choice IMO. Similarly, if they will be failing often, an if statement is better.

Performance of exceptions versus attribute lookups is not so different in Python, so I'd worry more about the logic of using exceptions/look-before-you-leap than about the performance aspects.

Ryan Ginstrom