Null pattern in Python underused?

2019-04-18 19:47发布

Every now and then I come across code like this:

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

Which seems, well, unpythonic, to me.

In Objective-C, you can send messages to nil and get nil as the answer. I've always thought that's quite handy. Of course it is possible to implement a Null pattern in Python, however judging from the results Google's given me, it seems this is not very widely used. Why's that?

Or even better—would it be a bad idea to let None.whatever return None instead of raising an exception?

11条回答
爷、活的狠高调
2楼-- · 2019-04-18 20:17

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.

查看更多
Root(大扎)
3楼-- · 2019-04-18 20:20

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.

查看更多
啃猪蹄的小仙女
4楼-- · 2019-04-18 20:24
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(...)
查看更多
相关推荐>>
5楼-- · 2019-04-18 20:26

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.

查看更多
Deceive 欺骗
6楼-- · 2019-04-18 20:27

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)
查看更多
beautiful°
7楼-- · 2019-04-18 20:29

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 :)

查看更多
登录 后发表回答