Would it be pythonic to use `or`, similar to how PHP would use `or die()`?

五迷三道 提交于 2020-01-02 02:53:09

问题


Is it pythonic to use or, similar to how PHP would use or die()?

I have been using
quiet or print(stuff)
instead of
if verbose: print(stuff)
lately.

I think it looks nicer, they do the same thing, and it saves on a line. Would one be better than the other in terms of performance?

The bytecode for both look pretty much the same to me, but I don't really know what I'm looking at...

or

  
  2           0 LOAD_FAST                0 (quiet)
              3 JUMP_IF_TRUE_OR_POP     15
              6 LOAD_GLOBAL              0 (print)
              9 LOAD_CONST               1 ('foo')
             12 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
        >>   15 POP_TOP
             16 LOAD_CONST               0 (None)
             19 RETURN_VALUE

vs if


  2           0 LOAD_FAST                0 (verbose)
              3 POP_JUMP_IF_FALSE       19

  3           6 LOAD_GLOBAL              0 (print)
              9 LOAD_CONST               1 ('bar')
             12 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             15 POP_TOP
             16 JUMP_FORWARD             0 (to 19)
        >>   19 LOAD_CONST               0 (None)
             22 RETURN_VALUE

回答1:


No, this is definitely not Pythonic. Many of the decision made along the way have been specifically to discourage this kind of coding.


The right way to write this is the obvious way:

if verbose:
    print(stuff)

Or, if computing stuff in itself isn't expensive/dangerous/irrevocable, just wrap print in a function that checks the flag:

def printv(*args, **kwargs):
    if verbose:
        print(*args, **kwargs)

… so you can just do this, which is about as concise as you're going to get:

printv(stuff)

Or, even better, use the logging module instead of reinventing the wheel.


But what if stuff is expensive, and you really want to save a line?

Well, you probably don't really need to save a line, and doing so is always sacrificing readability for brevity; "Readability Counts" is a key part of the Zen of Python. But Python does allow you to put a one-line suite on the same line as the condition:

if verbose: print(stuff)

As PEP 8 puts it, "sometimes it's okay", but it's "generally discouraged". As Guido put it in an email on the ideas mailing list, "If you must save a line, use a one-line if statement rather than something 'clever'. But if you must save a line, I probably don't want to read your code."

If you want this in an expression (which you shouldn't, as I'll explain below), or you want to technically follow PEP 8 to the letter while blatantly violating the spirit:

print(stuff) if verbose else None

So, what's not Pythonic about it?

For one thing, you're using the result of print in an expression, even though print has no useful result, and is only called for its side effects. This is misleading.

And in general, Python has only one side effect per line, and it happens as far to the left as possible, which makes it easy to skim code and see what's changing. This is reinforced by the strong divide between statements and expressions (and, e.g., assignments being statements), by methods with side effects idiomatically returning None instead of self, and so on.

But even ignoring all that, using and and or just for short-circuiting, rather than for their results, is potentially confusing and, at least in some people's minds, ugly. That's why the ternary expression (spam if eggs else beans) was added: to stop people from writing eggs and spam or beans. Why is it confusing? For one thing, it doesn't really read like English at all, unless you've been reading more Perl code than actual English. For another, it's very easy to accidentally use a valid value that happens to be falsey; you know not to do that, and to check for that, when you see an if, but you don't here. Also note that the print(stuff) if verbose else None makes it explicit that you are creating a value which you then do nothing with; explicit is always better than implicit, but especially so when you're doing something uncommon.


Finally, as for performance: (a) who cares, and (b) why not measure it instead of try to guess by reading bytecode you don't understand?

In [511]: quiet = True
In [512]: %timeit quiet or None
10000000 loops, best of 3: 48.2 ns per loop
In [513]: verbose=False
In [514]: %timeit if verbose: pass
10000000 loops, best of 3: 38.5 ns per loop

So there you go, in the fast-pass case, the if statement is actually about 20% faster, not slower—and they're both so fast that it's unlikely to ever affect your program anyway. If you're doing this a billion times in a tight loop and need to squeeze out that performance, you're going to want to lift the rest out of the loop, even if that means repeating yourself with two near-clones of the same code (especially considering that a loop without the prints is more likely to fit into cache, etc.).

If you want to know why, well, you'd have to look at the implementation of those bytecodes on the particular version of the particular implementation that you care about… but most likely, needing to do an extra POP_TOP instead of having one merged into the previous operation is part of the difference.




回答2:


I don't think this is pythonic. ("Explicit is better than implicit").

You can write

if verbose: print(stuff)

so if you desperately need to keep your line count down, you can.

The most pythonic way ("Readability counts.") would still be to use

if verbose:
    print(stuff)


来源:https://stackoverflow.com/questions/26664855/would-it-be-pythonic-to-use-or-similar-to-how-php-would-use-or-die

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!