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

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(...)
查看更多
做个烂人
3楼-- · 2019-04-18 20:36

PEP 336 - Make None Callable proposed a similar feature:

None should be a callable object that when called with any arguments has no side effect and returns None.

The reason for why it was rejected was simply "It is considered a feature that None raises an error when called."

查看更多
小情绪 Triste *
4楼-- · 2019-04-18 20:38

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.

查看更多
Bombasti
5楼-- · 2019-04-18 20:41

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?

查看更多
该账号已被封号
6楼-- · 2019-04-18 20:42

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:
    ...
查看更多
登录 后发表回答