views:

715

answers:

11
+12  A: 

Couldn't you do a try except? The Pythonic way says It is Easier to Ask for Forgiveness than Permission.

So:

try:
    if foo.bar.baz == 42:
        shiny_happy(...)
except AttributeError:
    pass #or whatever

Or do it without possibly silencing more exceptions than desired:

try:
    baz = foo.bar.baz
except AttributeError:
    pass # handle error as desired
else:
    if baz == 42:
        shiny_happy(...)
Skilldrick
You could, but my goal is to make the code simpler (or perhaps somewhat more elegant).
Tomas Brambora
It's definitely more pythonic that `if foo.bar is not None and foo.bar.baz == 42`, which seemed to be your issue.
Skilldrick
OK, according to the "pythonicity as written in a manual" it is. But that's my bad; I think "pythonic" is perhaps not the right word.Let's say elegant. Which is arguably not the right word either. :-)
Tomas Brambora
I think we're moving towards a philosophical debate here!
Skilldrick
The python way has always been and should always be to try and catch in place of explicitly checking for errors. If you assume that stuff works, it will run faster when it actually does. When it doesn't work, then it's ok that things take a little longer because it should be the exception to the rule.
Tor Valamo
That code is problematic, you are catching and ignoring not just when .bar doesn't have a .baz, but also unintentionally ignoring when foo doesn't have a .bar
Eloff
The original code was checking when foo doesn't have a bar: `if foo.bar is not None`. It didn't check to see if foo.bar had a baz.
Skilldrick
Squelching `AttributeError` like that is a *terrible* idea. This is much worse than the original code. What if `shiny_happy`, or code called from it, has a bug?
Jason Orendorff
@Skilldrick: you miss my point. What if foo doesn't have an attribute bar at all. What if it has a bar, but that bar doesn't have a .baz attribute? Worse yet what if shiny_happy() raise an AttributeError? You are catching (and ignoring) multiple possible errors unintentionally. That is just BAD programming. BAD because you don't say to the reader of the code how many of those error conditions you actually mean to ignore, and BAD because you do ignore them all, which may or may not be the correct thing to do. This code is indefensible, and it saddens me that it got so many upvotes.
Eloff
While I'd usually prefer code like in the question when it could be that simple (doing nothing when foo.bar is None being a *huge* part of that simplicity), I clarified Skilldrick's technique (which I use often in more complex error-handling scenarios), because it instantly dispels the major (and justified) complaint against it, without loss of usefulness (if anything, it clarifies what code is expected to throw).
Roger Pate
Sigh. This can still mask bugs. Suppose .bar or .baz has a getter.
Jason Orendorff
Jason: The essence of programming is in tradeoffs, and the example code in the question is abstract, so it's difficult to pin down requirements for it *because it doesn't have any*. This answer is one way to approach those tradeoffs, but of course you can *never* have a perfect solution, because There Is No Silver Bullet(tm). You've gone from saying "hiding errors in shiny_happy is a problem" to describing a concrete implementation issue that is both more easily detectable in other ways (such as unit tests for Foo) and much less common. That's essentially a different complaint.
Roger Pate
Eloff: With the updated code, assigning `bar = foo.bar` before the try-block and `baz = bar.baz` within the try-block addresses that. It also deals with foo.bar being a property. (But I'll let Skilldrick edit to include that if he wants. :P) And for those who keep upvoting, I'd like to make it clear that *even with* improvements, in *this instance* (and without more details that a real problem would have) the code in the question really is better than this answer; so take that into consideration.
Roger Pate
Roger: Regarding requirements, if the original code works, then Skilldrick's code, and yours, work the same *except for the bugs they might mask*. My essential complaint is unchanged: this style of programming is hard to get right (as evidenced here), and to the degree that you get it wrong, you're squelching exceptions. Narrowing the scope wherein you ignore errors is an improvement, but if you can eliminate it altogether, you should.
Jason Orendorff
For an example of where getting this wrong made a simple bug lead to very mysterious behavior, see: http://stackoverflow.com/questions/1799462/python-print-doesnt-work-script-hangs-endlessly ...Of course, all programming techniques can be incorrectly used.
Jason Orendorff
*There is no original code.* The code in the question is an example of a particular technique the OP has seen, taken out of context of all requirements. I said it twice already, but again remember that *I agree with you in an abstract way* and only included more information because *this* technique *is* helpful *in the right situation*.
Roger Pate
+4  A: 

I don't think it's a good idea. Here's why. suppose you have

foo.getValue()

Suppose getValue() returns a number, or None if not found. Now suppose foo was none by accident or a bug. As a result, it would return None, and continue, even if there's a bug.

In other words, you are no longer able to distinguish if there's no value (returns None) or if there's an error (foo was None to begin with). You are altering the return contract of a routine with a fact that is not under control of the routine itself, eventually overwriting its semantics.

Stefano Borini
Hmm... Not a bad point. But I think that's just a question of how you define the afformentioned contract. Either it could be "throw an exception" or "return Null".
Tomas Brambora
@Tomas Brambora: Correct. And almost everything is defined as "throw an exception" so that they can return *meaningful* `None`'s. Since we have a choice, we prefer to reserve `None` to be distinct from exceptional behavior.
S.Lott
None is not exceptional in this case. Why would it be ? if there's no value, it's not an exceptional situation, you just have no value, and returning None is the correct behavior.
Stefano Borini
+2  A: 

Here's why I don't think that's a good idea:

foo = Foo() // now foo is None

// if foo is None, I want to raise an exception because that is an error condition.
// The normal case is that I expect a foo back whose bar property may or may not be filled in.
// If it's not filled in, I want to set it myself.

if not foo.bar // Evaluates to true because None.bar is None under your paradigm, I think
  foo.bar = 42 // Now what?

How would you handle this case?

danben
In that case Null should probably raise an exception. I'm not saying you should be able to set Null attributes, just be able to return Null when comparing (i.e., getting the attribute value).
Tomas Brambora
+1  A: 

Personally I believe throwing an exception is what should happen. Lets say for some reason you are writing software for missiles in Python. Imagine the atomic bomb and lets say there was a method called explode(timeToExplode) and timeToExplode got passed in as None. I think you would be unhappy at the end of the day when you lost the war, because you didn't find this in testing.

Woot4Moo
Interesting. The only winning move is not to play.
balpha
Well, I don't really get your point. Would you be happier if you've passed None rather than Null? Somewhere you have to test for null-ness or none-ness anyway.
Tomas Brambora
I would be more happy if you properly handled nulls in the manner in which the language specifies. But hey that's just me and my quirkiness.
Woot4Moo
+8  A: 

The handiness comes at the expense of dumb mistakes not being detected at the earliest possible time, as close to the buggy line of code as possible.

I think the handiness of this particular feature would be occasional at best, whereas dumb mistakes happen all the time.


Certainly a SQL-like NULL would be bad for testing, which really banks on propositions being either true or false. Consider this code, from unittest.py:

class TestCase:
    ...
    def failUnless(self, expr, msg=None):
        """Fail the test unless the expression is true."""
        if not expr: raise self.failureException, msg

Now suppose I have a test that does this:

conn = connect(addr)
self.failUnless(conn.isOpen())

Suppose connect erroneously returns null. If I'm using the "null pattern", or the language has it built-in, and conn is null, then conn.isOpen() is null, and not conn.isOpen() is null too, so the assertion passes, even though the connection clearly is not open.

I tend to think NULL is one of SQL's worst features. And the fact that null silently passes for an object of any type in other languages is not much better. (Tony Hoare called null references “my billion-dollar mistake”.) We need less of that sort of thing, not more.

Jason Orendorff
+12  A: 

PEP 336 - Make None Callable might answer your question. The reason for why it was rejected was simply "It is considered a feature that None raises an error when called."

mozillalives
The general python philosophy is found by `import this`. Specifically: "In the face of ambiguity, refuse the temptation to guess." Python also goes out of it's way to make sure that None is returned if no explicit return statement is found. The implementation would have made it easy to return the last thing on the stack, like lisp, or the first argument to the callable, like smalltalk.
Shane Holloway
A: 
foo = Foo()
...
if foo.bar is not None and foo.bar.baz == 42:
   shiny_happy(...)

The above can be cleaned up using the fact that None resolves to False:

if foo.bar and foo.bar.baz == 42:
    shiny_happy(...)
else:
    not_happy(...)
manifest
Except it's not the same. If foo.bar happens to be an empty list or 0, you've got trouble.
James Hopkin
It really is counter-intuitive for None to be called upon and return anything. After all None is nothing, so why would you be able to do anything with it? The "elegance" lost should only be due to the fact that bar is not guaranteed to be something.
manifest
In the question example, foo very obviously is not a list or a number, it is an object. If it were a list or a number, than yes, this would have to be treated differently.
manifest
@James, if `foo.bar` is an empty list or 0, then `foo.bar.baz` will fail anyway, so we *won't be happy*..
poke
It's quite possible that `foo.bar` is a container object that has a `baz` attribute or property. It will evaluate as `False` depending on the `__nonzero__` special method or just on `len(foo.bar)` being zero. As James says, the `if foo.bar` shortcut is not safe in general.
Scott Griffiths
+9  A: 

I'm sorry, but that code is pythonic. I think most would agree that "explicit is better than implicit" in Python. Python is a language that is easy to read compared to most, and people should not defeat that by writing cryptic code. Make the meaning very clear.

foo = Foo()
...
if foo.bar is not None and foo.bar.baz == 42:
    shiny_happy(...)

In this code sample, it is clear that foo.bar is sometimes None on this code path, and that we only run shiny_happy() if it is not None, and .baz == 42. Very clear to anyone what is going on here and why. The same can not be said for the null pattern, or the try ... except code in one of the answers posted here. It's one thing if your language, like Objective-C or javascript enforces a null pattern, but in a language where it is not used at all, it will just create confusion and code that is difficult to read. When programming in python, do as the pythonistas do.

Eloff
A: 

I am not a python programmer (just starting to learn the language) but this seems like the never-ending discussion on when to return error or throw exception, and the fact is that (of course this is only an opinion) it depends.

Exceptions should be used for exceptional conditions, and as such move the checks for rare situations outside of the main block of code. They should not be used when a not-found value is a common condition (think of requesting the left child of a tree, in many cases --all leaves-- it will be null). There is yet again a third situation with functions that return sets of values, where you might just want to return a valid empty set to ease the code.

I believe that following the above advice, code is much more legible. When the situation is rare you do not need to worry about null (an exception will be called) so less used code is moved out of the main block.

When null is a valid return value, processing it is within the main flow of control, but that is good, as it is a common situation and as such part of the main algorithm (do not follow a null edge in a graph).

In the third case, when requesting values from a function that can possibly return no values, returning an empty set simplifies the code: you can assume that the returned container exists and process all found elements without adding the extra checks in the code.

Then again, that is just an opinion, but being mine I tend to follow it :)

David Rodríguez - dribeas
I can't see why an exception should be rare. An exception should be raised if there is an error, which can be very frequent depending the situation and the context.Of course, usually errors are exceptional, but some times that's not the case.To me, the most pythonic way will be raising an error in Foo() if it doesn't return a correct value...
Khelben
Exceptions are meant (at least in most languages I know, of which python is not one yet) to deal with exceptional situations. In most cases there is a cost associated with the use of exceptions as compared to checking return values. The use of exception not only has a cost, but breaks the flow of execution of the function making it harder to read, or (if each function call is wrapped in a try) adds quite a lot of error control code intermixed with the algorithm, again difficulting the reading.
David Rodríguez - dribeas
The point there is whether null (None) is an expected result of the function or else an error. If it is an error, then an exception should be thrown, but if it is expected (requesting the left child in a tree node is expected to yield null in many cases) then exceptions should not be used. In the case of retrieving data, while requesting an element from a database by primary key (with a key that was previously obtained) failing to get the element is an error, and as such should be an exception.
David Rodríguez - dribeas
On the other hand, finding no items when searching for a given property from an inventory (say all green cars in stock in an autodealer) is not an error but rather a valid result: there are no green cars now. That condition should not trigger an exception, but rather return an empty set --which is a valid result semantically.
David Rodríguez - dribeas
+2  A: 

While I wholeheartedly agree with other answers here that say that it's a good thing that None raises an exception when asked for a member, this is a little pattern I sometimes use:

getattr(foo.bar, 'baz', default_value)
Virgil Dupras
+1  A: 

As others have said, PEP 336 describes why this is the behavior.

Adding something like Groovy's "safe navigation operator" (?.) could perhaps make things more elegant in some cases:

foo = Foo()

if foo.bar?.baz == 42:
    ...
Steve Losh