What Is More 'Pythonic' for 'Not'

Which is more pythonic in a for loop: zip or enumerate?

No doubt, zip is more pythonic. It doesn't require that you use a variable to store an index (which you don't otherwise need), and using it allows handling the lists uniformly, while with enumerate, you iterate over one list, and index the other list, i.e. non-uniform handling.

However, you should be aware of the caveat that zip runs only up to the shorter of the two lists. To avoid duplicating someone else's answer I'd just include a reference here: someone else's answer.

@user3100115 aptly points out that in python2, you should prefer using itertools.izip over zip, due its lazy nature (faster and more memory efficient). In python3 zip already behaves like py2's izip.

Is there an operation for not less than or not greater than in python?

Instead of a == 0 or a > 0, simply use a >= 0.

See https://docs.python.org/library/stdtypes.html#comparisons for a complete list of available comparison operators.

Python `if x is not None` or `if not x is None`?

There's no performance difference, as they compile to the same bytecode:

>>> import dis
>>> dis.dis("not x is None")
1 0 LOAD_NAME 0 (x)
2 LOAD_CONST 0 (None)
4 COMPARE_OP 9 (is not)
6 RETURN_VALUE
>>> dis.dis("x is not None")
1 0 LOAD_NAME 0 (x)
2 LOAD_CONST 0 (None)
4 COMPARE_OP 9 (is not)
6 RETURN_VALUE

Stylistically, I try to avoid not x is y, a human reader might misunderstand it as (not x) is y. If I write x is not y then there is no ambiguity.

What does Pythonic mean?

Exploiting the features of the Python language to produce code that is clear, concise and maintainable.

Pythonic means code that doesn't just get the syntax right, but that follows the conventions of the Python community and uses the language in the way it is intended to be used.

This is maybe easiest to explain by negative example, as in the linked article from the other answers. Examples of un-Pythonic code often come from users of other languages, who instead of learning a Python programming patterns such as list comprehensions or generator expressions, attempt to crowbar in patterns more commonly used in C or Java. Loops are particularly common examples of this.

For example, in Java I might use

for (int index = 0; index < items.length; index++) {
items[index].performAction();
}

In Python we can try and replicate this using while loops, but it would be cleaner to use:

for item in items:
item.perform_action()

Or, even a generator expression

(item.some_attribute for item in items)

So essentially when someone says something is un-Pythonic, they are saying that the code could be rewritten in a way that is a better fit for Python's coding style.

Typing import this at the command line gives a summary of Python principles. Less well known is that the source code for import this is decidedly, and by design, un-Pythonic! Take a look at it for an example of what not to do.

Most pythonic way of checking if a Key is in a dictionary and Value is not None

Just use dict.get method with optional default parameter. It returns d[key] if key exists in the dictionary d and default value otherwise:

In [1]: d = {1: 'A', 2: None}

In [2]: d.get(1)
Out[2]: 'A'

In [3]: d.get(2)

In [4]: d.get(1) is not None
Out[4]: True

In [5]: d.get(2) is not None
Out[5]: False

In [6]: d.get(3) is not None
Out[6]: False

For your case:

if self.__args.get('OptionalArg') is not None:
# Do something with the OptionalArg only
pass
else:
# Do something with all the arguments that are not None.
pass

Python if not == vs if !=

Using dis to look at the bytecode generated for the two versions:

not ==

  4           0 LOAD_FAST                0 (foo)
3 LOAD_FAST 1 (bar)
6 COMPARE_OP 2 (==)
9 UNARY_NOT
10 RETURN_VALUE

!=

  4           0 LOAD_FAST                0 (foo)
3 LOAD_FAST 1 (bar)
6 COMPARE_OP 3 (!=)
9 RETURN_VALUE

The latter has fewer operations, and is therefore likely to be slightly more efficient.


It was pointed out in the commments (thanks, @Quincunx) that where you have if foo != bar vs. if not foo == bar the number of operations is exactly the same, it's just that the COMPARE_OP changes and POP_JUMP_IF_TRUE switches to POP_JUMP_IF_FALSE:

not ==:

  2           0 LOAD_FAST                0 (foo)
3 LOAD_FAST 1 (bar)
6 COMPARE_OP 2 (==)
9 POP_JUMP_IF_TRUE 16

!=

  2           0 LOAD_FAST                0 (foo)
3 LOAD_FAST 1 (bar)
6 COMPARE_OP 3 (!=)
9 POP_JUMP_IF_FALSE 16

In this case, unless there was a difference in the amount of work required for each comparison, it's unlikely you'd see any performance difference at all.


However, note that the two versions won't always be logically identical, as it will depend on the implementations of __eq__ and __ne__ for the objects in question. Per the data model documentation:

There are no implied relationships among the comparison operators. The
truth of x==y does not imply that x!=y is false.

For example:

>>> class Dummy(object):
def __eq__(self, other):
return True
def __ne__(self, other):
return True

>>> not Dummy() == Dummy()
False
>>> Dummy() != Dummy()
True

Finally, and perhaps most importantly: in general, where the two are logically identical, x != y is much more readable than not x == y.

Pythonic way to combine for-loop and if-statement

You can use generator expressions like this:

gen = (x for x in xyz if x not in a)

for x in gen:
print(x)

Most pythonic way to delete a file which may not exist

A more pythonic way would be:

try:
os.remove(filename)
except OSError:
pass

Although this takes even more lines and looks very ugly, it avoids the unnecessary call to os.path.exists() and follows the python convention of overusing exceptions.

It may be worthwhile to write a function to do this for you:

import os, errno

def silentremove(filename):
try:
os.remove(filename)
except OSError as e: # this would be "except OSError, e:" before Python 2.6
if e.errno != errno.ENOENT: # errno.ENOENT = no such file or directory
raise # re-raise exception if a different error occurred

[] and {} vs list() and dict(), which is better?

In terms of speed, it's no competition for empty lists/dicts:

>>> from timeit import timeit
>>> timeit("[]")
0.040084982867934334
>>> timeit("list()")
0.17704233359267718
>>> timeit("{}")
0.033620194745424214
>>> timeit("dict()")
0.1821558326547077

and for non-empty:

>>> timeit("[1,2,3]")
0.24316302770330367
>>> timeit("list((1,2,3))")
0.44744206316727286
>>> timeit("list(foo)", setup="foo=(1,2,3)")
0.446036018543964
>>> timeit("{'a':1, 'b':2, 'c':3}")
0.20868602015059423
>>> timeit("dict(a=1, b=2, c=3)")
0.47635635255323905
>>> timeit("dict(bar)", setup="bar=[('a', 1), ('b', 2), ('c', 3)]")
0.9028228448029267

Also, using the bracket notation lets you use list and dictionary comprehensions, which may be reason enough.

Why print statement is not pythonic?

Looks to me like yours is a debate, not a question -- are you really going to accept an answer that shows how deeply and badly wrong you were in your assertions?!

On to your debating points:

There are other operators, such as
import which we write as a statement,
though their functionality is actually
duplicated with a function __import__

Absolutely wrong: function __import__ (like every other function -- and operator, for that matter) binds no names in the scope of "caller" (code containing it) -- any "thingie" that binds names in the "caller's scope" must be a statement (just like assignment, def, and call). Your "point" appears to totally miss the extremely deep and crucial distinction that Python draws between statements and expressions -- one may reasonably dislike this distinction, but ignoring it is, most obviously, simply wrong.

Python statements are things the Python compiler must be specifically aware of -- they may alter the binding of names, may alter control flow, and/or may need to be entirely removed from the generated bytecode in certain conditions (the latter applies to assert). print was the only exception to this assertion in Python 2; by removing it from the roster of statements, Python 3 removes an exception, makes the general assertion "just hold", and therefore is a more regular language. Special cases are not special enough to break the rules has long been a Pythonic tenet (do import this at an interactive interpreter's >>> prompt to see "the Zen of Python" displayed), and this change to the language removes a violation of this tenet that had to remain for many years due to an early, erroneous design decision.

To beginners, the operator print does
not belong to the general application
logic. To them it's the mysterious
operator which is a culmination of
their program. They expect it to look
differently.

Curing beginners of their misconceptions as early as feasible is a very good thing.

All the beginner books which were
describing basic Python 2.x are now
guaranteed to be broken from the fist
example. Certainly, languages
sometimes changes, but changes are
usually less visible to novices.

Languages rarely change in deep and backwards-incompatible ways (Python does it about once a decade) and few language features are "highly visible to novices", so the total number of observations is small -- yet even within that tiny compass we can easily find counter-examples, where a feature highly visible to beginners was just so badly designed that removing it was well worth the disruption. For example, modern dialects of Basic, such as Microsoft's Visual Basic, don't use explicit user-entered line numbers, a "feature" that was both terrible and highly visible to absolutely everybody since it was mandatory in early dialects of Basic. Modern variants of Lisp (from Scheme onwards) don't use dynamic scoping, a misfeature that was sadly highly visible (usually manifesting as hard-to-understand bugs in their code) to beginners, basically as soon as they started writing functions in Lisp 1.5 (I once was a beginner in that and can testify to how badly it bit me).

It's not immediately obvious to me
that a functionality of print can be
duplicated on an application level.
For example, sometimes I would like to
redirect print from a console as a
modal OS dialog.

Not sure I follow this "point". Just change sys.stdout to your favorite pseudo-file object and redirect to your heart's contents -- you have the option of monkey patching the built-in function print (which you never had in Python 2), but nobody's twisting your arm and forcing you to do so.

While people say it's hard to rewrite
all print statements to a function,
they have forced every Python 2.x
developer to do exactly that for all
their projects. Good, it's not hard
with automatic converter.

The 2to3 tool does indeed take care of all such easy surface incompatibilities -- no sweat (and it needs to be run anyway to take care of quite a few more besides print, so people do use it extensively). So, what's your "point" here?

Everyone who enjoys having an ability
to manipulate function print would be
just as well-served if print was a
statement wrapping function print.

Such an arrangement would not, per se, remove an unnecessary keyword (and most especially, an unjustified irregularity, as I explained above: a statement that has no good reason to be a statement because there is absolutely no need for the compiler to be specially aware of it in any way, shape, or form!). It's far from clear to me that having such an underlying function would add any real value, but if you have real use cases you can certainly propose the case in the Python Ideas mailing list -- such an underlying function, if proven to be precious indeed, could be retrofitted to be used by the print statement in Python 2.7 as well as by the print function in Python 3.2.

However, consider a typical case in which one might want to monkey-patch the built-in print: adding keyword arguments to allow fancy tweaks. How would the __print__ function you're apparently proposed ever ge those KW arguments from a __print__ statement? Some funkier syntax yet than the horrors of >> myfile and the trailing comma...?! With print as a function, keyword arguments follow just the perfectly normal and ordinary rules that apply to every function and function call -- bliss!

So, in summary, it's more Pythonic for print to be a function because it removes anomalies, special cases, and any need for weird exceptional syntax -- simplicity, regularity, and uniformity are Python's trademark.



Related Topics



Leave a reply



Submit