I claimed that uses of "is" where it is needed for correctness
are quite rare. Let me back that up with a little data here.
Just as a random supply of Python code, let's look at
the first four Python modules where the name starts
with a letter from the Python standard modules list
https://docs.python.org/3/py-modindex.html :
abc.py aifc.py argparse.py ast.py (The array module appears to be in C)
Strip out PyDoc and string literals and just grep
for " is " (code included below if you'd like to try
it yourself). Look at those lines - how many of those
uses of "is" are needed for correctness, where the "is"
is really doing something, and how many would work
fine with ==? The resulting lines of code are included below.
There's about 90 uses of is/is-not in this sample.
100% of these uses would work correctly using ==.
Not a single one of these uses actually relies on the "is"
computation for correctness.
PEP8 has forced code to use "is" so much, I think people have
gotten the impression that "is" was really doing something when
that's just not true. These uses of "is" are sort of decorative.
There are cases where the "is" computation is needed, but
they are quite rare.
I am sceptical of the benefit of requiring "is" in all these places where it is not needed.
The other thing I notice looking at this sample, is that
really the use of "is" is dominated by comparisons to None.
I would be satisfied to relax PEP8 just to allow == with None.
As a practical matter, that's the case that dominates.
For the curious, here are the three categories of "is" use
you'll see in the sample.
1. By the far the most common use of is in here is comparing to None,
like we see in the very first line. PEP8 refers to the values False/True
also, but as a practical matter, None is by far the common singleton
for this pattern. There is not a single True/False comparison in this
sample.
if file is not None:
2. A secondary use of "is" is checking for a particular type or class,
like on these two lines:
if type(items) is list:
if type_func is FileType:
3. Lastly we see "is" checking to see if a value matches
a constant, like this
if self.heading is not SUPPRESS and self.heading is not None:
if operator_precedence is not _Precedence.FACTOR:
I assume that if we have some module.CONST = 2000 constant,
the module can accept client uses of either module.CONST or 2000,
though clearly using module.CONST is better stylistically.
---
' is ' lines from: abc.py aifc.py argparse.py ast.py
if file is not None:
self._aifc = 1 # AIFF-C is default
if self._file is None:
if self._form_length_pos is not None:
if self._form_length_pos is not None:
if mode is None:
if items is None:
if type(items) is list:
if width is None:
if self.parent is not None:
if self.parent is not None:
if self.heading is not SUPPRESS and self.heading is not None:
if text is not SUPPRESS and text is not None:
if usage is not SUPPRESS:
if action.help is not SUPPRESS:
if part and part is not SUPPRESS])
if prefix is None:
if usage is not None:
elif usage is None and not actions:
elif usage is None:
if prefix is not None:
if prefix is not None:
if action.help is SUPPRESS:
text = ''.join([item for item in parts if item is not None])
if action.metavar is not None:
elif action.choices is not None:
if action.nargs is None:
if params[name] is SUPPRESS:
if params.get('') is not None:
if action.default is not SUPPRESS:
if argument is None:
if self.argument_name is None:
if help is not None and default is not None:
if const is not None and nargs != OPTIONAL:
if const is not None and nargs != OPTIONAL:
if count is None:
if version is None:
if kwargs.get('') is None:
if self.dest is not SUPPRESS:
if arg is not None])
if action.dest == dest and action.default is not None:
elif self.argument_default is not None:
if type_func is FileType:
if dest is None:
if prog is None:
if self._subparsers is not None:
if kwargs.get('') is None:
if args is None:
if namespace is None:
if action.dest is not SUPPRESS:
if action.default is not SUPPRESS:
if self.fromfile_prefix_chars is not None:
if option_tuple is None:
if argument_values is not action.default:
if argument_values is not SUPPRESS:
if action is None:
if explicit_arg is not None:
if (action.default is not None and
action.default is getattr(namespace, action.dest)):
if action.help is not SUPPRESS]
if match is None:
if msg is None:
if match is not None:
if nargs is None:
if self.usage is None:
if action.default is not None:
if action.choices is not None and value not in action.choices:
if file is None:
if file is None:
if file is None:
elif feature_version is None:
if indent is not None:
if value is None and getattr(cls, name, ...) is None:
if value is None and getattr(cls, name, ...) is None:
if indent is not None and not isinstance(indent, str):
if value is not None or (
if getattr(node, '', None) is None:
if getattr(node, '', None) is None:
and (end_lineno := getattr(child, "", 0)) is not None
if node.end_lineno is None or node.end_col_offset is None:
if type_name is None:
if type_name is not None:
if value is None:
if new_node is None:
if cls is Ellipsis:
elif value is ...:
if k is None:
if operator_precedence is not _Precedence.FACTOR:
if node.arg is None:
----
stripcode.py
#!/usr/bin/env python3
"""
Echo python files input with most of the strings
and comments removed, so you can grep for code patterns.
Nick Parlante
This code is placed in the public domain
"""
import sys
import re
def strip_code(code):
"""Return code text with most string literals and comments removed"""
code = re.sub(r"'''.*?'''", "''", code, flags=re.DOTALL)
code = re.sub(r'""".*?"""', "''", code, flags=re.DOTALL)
code = re.sub(r"'.*?'", "''", code) # won't work right with \'
code = re.sub(r'".*?"', '""', code)
code = re.sub(r'^\s*#.*$', '', code, flags=re.MULTILINE) # only comments on line by self
return code
def print_strip(filename):
"""Print stripped version of given file"""
with open(filename) as f:
print(strip_code(f.read()), end='')
def main():
args = sys.argv[1:]
for fname in args:
print_strip(fname)
if __name__ == '__main__':
main()