PEP8 mandatory-is rule
Hi there python-ideas - I've been teaching Python as a first programming language for a few years, and from that experience I want to propose a change to PEP8. I'm sure the default position for PEP8 is to avoid changing it. However, for this one rule I think a good case can be made to make it optional, so let me know what you think. Let me start with what I've learned from teaching students in Java and now in Python. In Java, you use == for ints, but you need to use equals() for strings. Of course students screw this up constantly, using == in a context that calls for equals() and their code does not work right. Then for Java arrays a different comparison function is required, and so it goes. To teach comparisons in Python, I simply say "just use ==" - it works for ints, for strings, even for lists. Students are blown away by how nice and simple this is. This is how things should work. Python really gets this right. So what is the problem? The problem for Python is what I will call the "mandatory-is" rule in PEP8, which reads: Comparisons to singletons like None should always be done with is or is not, never the equality operators. For the students, this comes up in the first week of the course with lines like "if x == None:" which work perfectly with == but should use is/is-not for PEP8 conformance. My guess is that this rule is in PEP8 because, within a Python implementation, it is within the programmer's mental model that, say, False is a singleton. The mandatory-is rule is in PEP8 to reinforce that mental model by requiring the is operator. Plus it probably runs a tiny bit faster. However, for "regular" Python code, not implementing Python, forcing the use of is instead of the simpler == is unneeded and unhelpful (and analogously forcing "is not" when != works correctly). What is the benefit of forcing the is operator there? I would say it spreads an awareness of the details of how certain values are allocated within Python. That's not much of a benefit, and it's kind of circular. Like if programmers were permitted to use ==, they wouldn't need to know the details of how Python allocates those values. Being shielded from implementation details is a Python strength - think of the Java vs. Python story above. Is Java better because it builds an awareness in the programmer of the different comparison functions for different types? Of course not! Python is better in that case because it lets the programmer simply use == and not think about those details. Understanding the singleton strategy is important in some corners of coding, but forcing the is operator on all Python code is way out of proportion to the benefit. As a practical matter, the way this comes up for my students is that IDEs by default will put warning marks around PEP8 violations in their code. Mostly this IDE-coaching is very helpful for students learning Python. For example, It's great that beginning Python programmers learn to put one space around operators right from the first day. Having taught thousands of introductory Python students, the one PEP8 rule that causes problems is this mandatory-is rule. As a teacher, this is especially jarring since the "just use ==" rule is so effortless to use correctly. In contrast, the mandatory-is rule adds a little pause where the programmer should think about which comparison operator is the correct one to use. It's not hard, but it feels unnecessary. As a contrasting example, in the language C, programmers need to understand == vs. is right from the first day. You can't get anything done in C without understanding that distinction. However that is just not true for regular (not-Python-implementation) Python code, where == works correctly for the great majority of cases. Here is my proposal: Add the following parenthetical to the mandatory-is rule: (this rule is optional for code that is not part of an implementation of Python). So in effect, programmers outside of a Python implementation can choose to use == or is for the "if x == None:" case. In this way, PEP8 conforming code before the change is still conforming. Moving forward, I would expect that regular code will trend towards using == in such a case, reserving is for the rare cases where it is needed for correctness. PEP8 was originally just for Python implementations, so why is this change needed? Because as a practical matter, the vast majority of code that is using PEP8 is not part of a Python implementation. This may not have been the original mission of PEP8, but it is how things have worked out. Now we are in a situation where the rules in PEP8 are sent out to this ocean of Python programmers of many different ability levels writing regular code that is not a Python implementation. One could imagine a separate PEP800 style guide for regular code, but we don't need to do that, because in almost all cases PEP8 works great for regular code. I have taught thousands of new Python programmers, and the only place where PEP8 serves them poorly is this mandatory-is rule. Therefore instead of a separate style guide for regular code, I propose an exception for this one problem rule. Ultimately this comes down to the question - should PEP8 push regular, not-Python-implementation code to use is for singletons in cases where == works perfectly? Seeing how effortless it is for programmers to use == as their first choice, I think PEP8 should allow that practice. Best, Nick
From my point of view as someone who sometimes help Scientist write Python, this is a no go, there are too many cases where == and is are different. $ ipython Python 3.8.5 | packaged by conda-forge | (default, Sep 16 2020, 17:43:11) Type 'copyright', 'credits' or 'license' for more information IPython 7.25.0 -- An enhanced Interactive Python. Type '?' for help. In [1]: import numpy as np In [2]: a = np.array([True, False, None]) In [3]: a == True, a == False, a == None Out[3]: (array([ True, False, False]), array([False, True, False]), array([False, False, True])) In [4]: a is True, a is False, a is None Out[4]: (False, False, False) if `a == True` can even raise errors when in ifs: In [5]: if a == True: ...: pass ...: --------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-5-59a850ef5f8d> in <module> ----> 1 if a == True: 2 pass 3 ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all() Basic types are just too simple to expose the real need between `==` and `is`, but that's not a reason not to give the right advice from the start. IMHO It would be like teaching English and saying that it's ok not to put s after nouns when there is a number in front as it's obvious there are many. I do understand your concern, but I believe that would be just pushing the problem to later, when it would be much more difficult to explain and have students get a wrong mental model from the start, which is really hard to overcome. -- Matthias On Mon, 30 Aug 2021 at 11:45, Nick Parlante <nick@cs.stanford.edu> wrote:
Hi there python-ideas - I've been teaching Python as a first programming language for a few years, and from that experience I want to propose a change to PEP8. I'm sure the default position for PEP8 is to avoid changing it. However, for this one rule I think a good case can be made to make it optional, so let me know what you think.
Let me start with what I've learned from teaching students in Java and now in Python. In Java, you use == for ints, but you need to use equals() for strings. Of course students screw this up constantly, using == in a context that calls for equals() and their code does not work right. Then for Java arrays a different comparison function is required, and so it goes. To teach comparisons in Python, I simply say "just use ==" - it works for ints, for strings, even for lists. Students are blown away by how nice and simple this is. This is how things should work. Python really gets this right.
So what is the problem?
The problem for Python is what I will call the "mandatory-is" rule in PEP8, which reads:
Comparisons to singletons like None should always be done with is or is not, never the equality operators.
For the students, this comes up in the first week of the course with lines like "if x == None:" which work perfectly with == but should use is/is-not for PEP8 conformance.
My guess is that this rule is in PEP8 because, within a Python implementation, it is within the programmer's mental model that, say, False is a singleton. The mandatory-is rule is in PEP8 to reinforce that mental model by requiring the is operator. Plus it probably runs a tiny bit faster.
However, for "regular" Python code, not implementing Python, forcing the use of is instead of the simpler == is unneeded and unhelpful (and analogously forcing "is not" when != works correctly). What is the benefit of forcing the is operator there? I would say it spreads an awareness of the details of how certain values are allocated within Python. That's not much of a benefit, and it's kind of circular. Like if programmers were permitted to use ==, they wouldn't need to know the details of how Python allocates those values. Being shielded from implementation details is a Python strength - think of the Java vs. Python story above. Is Java better because it builds an awareness in the programmer of the different comparison functions for different types? Of course not! Python is better in that case because it lets the programmer simply use == and not think about those details. Understanding the singleton strategy is important in some corners of coding, but forcing the is operator on all Python code is way out of proportion to the benefit.
As a practical matter, the way this comes up for my students is that IDEs by default will put warning marks around PEP8 violations in their code. Mostly this IDE-coaching is very helpful for students learning Python. For example, It's great that beginning Python programmers learn to put one space around operators right from the first day. Having taught thousands of introductory Python students, the one PEP8 rule that causes problems is this mandatory-is rule.
As a teacher, this is especially jarring since the "just use ==" rule is so effortless to use correctly. In contrast, the mandatory-is rule adds a little pause where the programmer should think about which comparison operator is the correct one to use. It's not hard, but it feels unnecessary.
As a contrasting example, in the language C, programmers need to understand == vs. is right from the first day. You can't get anything done in C without understanding that distinction. However that is just not true for regular (not-Python-implementation) Python code, where == works correctly for the great majority of cases.
Here is my proposal:
Add the following parenthetical to the mandatory-is rule: (this rule is optional for code that is not part of an implementation of Python).
So in effect, programmers outside of a Python implementation can choose to use == or is for the "if x == None:" case. In this way, PEP8 conforming code before the change is still conforming. Moving forward, I would expect that regular code will trend towards using == in such a case, reserving is for the rare cases where it is needed for correctness.
PEP8 was originally just for Python implementations, so why is this change needed? Because as a practical matter, the vast majority of code that is using PEP8 is not part of a Python implementation. This may not have been the original mission of PEP8, but it is how things have worked out.
Now we are in a situation where the rules in PEP8 are sent out to this ocean of Python programmers of many different ability levels writing regular code that is not a Python implementation. One could imagine a separate PEP800 style guide for regular code, but we don't need to do that, because in almost all cases PEP8 works great for regular code. I have taught thousands of new Python programmers, and the only place where PEP8 serves them poorly is this mandatory-is rule. Therefore instead of a separate style guide for regular code, I propose an exception for this one problem rule.
Ultimately this comes down to the question - should PEP8 push regular, not-Python-implementation code to use is for singletons in cases where == works perfectly? Seeing how effortless it is for programmers to use == as their first choice, I think PEP8 should allow that practice.
Best,
Nick _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/JWLKBT... Code of Conduct: http://python.org/psf/codeofconduct/
To argue that == is unreliable, I think Matthias has the best, non-contrived example with numpy, which I did not know about:
x = np.array([1, 2, 3]) x == None array([False, False, False])
This certainly is a good example of == not being reliable. Point taken there. Are there other such classes? Like if I write code that just uses Python and its standard library classes, can I get a value where == None is flaky like the numpy example? I wouldn't think so, just as a matter of principle-of-least-surprise API design. Anyway, that would be very interesting example to argue against my claim that == None is reliable. Best, Nick On Mon, Aug 30, 2021 at 12:06 PM Matthias Bussonnier < bussonniermatthias@gmail.com> wrote:
From my point of view as someone who sometimes help Scientist write Python, this is a no go, there are too many cases where == and is are different.
$ ipython Python 3.8.5 | packaged by conda-forge | (default, Sep 16 2020, 17:43:11) Type 'copyright', 'credits' or 'license' for more information IPython 7.25.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]: import numpy as np
In [2]: a = np.array([True, False, None])
In [3]: a == True, a == False, a == None Out[3]: (array([ True, False, False]), array([False, True, False]), array([False, False, True]))
In [4]: a is True, a is False, a is None Out[4]: (False, False, False)
if `a == True` can even raise errors when in ifs:
In [5]: if a == True: ...: pass ...: --------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-5-59a850ef5f8d> in <module> ----> 1 if a == True: 2 pass 3
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
Basic types are just too simple to expose the real need between `==` and `is`, but that's not a reason not to give the right advice from the start. IMHO It would be like teaching English and saying that it's ok not to put s after nouns when there is a number in front as it's obvious there are many.
I do understand your concern, but I believe that would be just pushing the problem to later, when it would be much more difficult to explain and have students get a wrong mental model from the start, which is really hard to overcome.
-- Matthias
On Mon, 30 Aug 2021 at 11:45, Nick Parlante <nick@cs.stanford.edu> wrote:
Hi there python-ideas - I've been teaching Python as a first programming language for a few years, and from that experience I want to propose a change to PEP8. I'm sure the default position for PEP8 is to avoid changing it. However, for this one rule I think a good case can be made to make it optional, so let me know what you think.
Let me start with what I've learned from teaching students in Java and now in Python. In Java, you use == for ints, but you need to use equals() for strings. Of course students screw this up constantly, using == in a context that calls for equals() and their code does not work right. Then for Java arrays a different comparison function is required, and so it goes. To teach comparisons in Python, I simply say "just use ==" - it works for ints, for strings, even for lists. Students are blown away by how nice and simple this is. This is how things should work. Python really gets this right.
So what is the problem?
The problem for Python is what I will call the "mandatory-is" rule in PEP8, which reads:
Comparisons to singletons like None should always be done with is or is not, never the equality operators.
For the students, this comes up in the first week of the course with lines like "if x == None:" which work perfectly with == but should use is/is-not for PEP8 conformance.
My guess is that this rule is in PEP8 because, within a Python implementation, it is within the programmer's mental model that, say, False is a singleton. The mandatory-is rule is in PEP8 to reinforce that mental model by requiring the is operator. Plus it probably runs a tiny bit faster.
However, for "regular" Python code, not implementing Python, forcing the use of is instead of the simpler == is unneeded and unhelpful (and analogously forcing "is not" when != works correctly). What is the benefit of forcing the is operator there? I would say it spreads an awareness of the details of how certain values are allocated within Python. That's not much of a benefit, and it's kind of circular. Like if programmers were permitted to use ==, they wouldn't need to know the details of how Python allocates those values. Being shielded from implementation details is a Python strength - think of the Java vs. Python story above. Is Java better because it builds an awareness in the programmer of the different comparison functions for different types? Of course not! Python is better in that case because it lets the programmer simply use == and not think about those details. Understanding the singleton strategy is important in some corners of coding, but forcing the is operator on all Python code is way out of proportion to the benefit.
As a practical matter, the way this comes up for my students is that IDEs by default will put warning marks around PEP8 violations in their code. Mostly this IDE-coaching is very helpful for students learning Python. For example, It's great that beginning Python programmers learn to put one space around operators right from the first day. Having taught thousands of introductory Python students, the one PEP8 rule that causes problems is this mandatory-is rule.
As a teacher, this is especially jarring since the "just use ==" rule is so effortless to use correctly. In contrast, the mandatory-is rule adds a little pause where the programmer should think about which comparison operator is the correct one to use. It's not hard, but it feels unnecessary.
As a contrasting example, in the language C, programmers need to understand == vs. is right from the first day. You can't get anything done in C without understanding that distinction. However that is just not true for regular (not-Python-implementation) Python code, where == works correctly for the great majority of cases.
Here is my proposal:
Add the following parenthetical to the mandatory-is rule: (this rule is optional for code that is not part of an implementation of Python).
So in effect, programmers outside of a Python implementation can choose to use == or is for the "if x == None:" case. In this way, PEP8 conforming code before the change is still conforming. Moving forward, I would expect that regular code will trend towards using == in such a case, reserving is for the rare cases where it is needed for correctness.
PEP8 was originally just for Python implementations, so why is this change needed? Because as a practical matter, the vast majority of code that is using PEP8 is not part of a Python implementation. This may not have been the original mission of PEP8, but it is how things have worked out.
Now we are in a situation where the rules in PEP8 are sent out to this ocean of Python programmers of many different ability levels writing regular code that is not a Python implementation. One could imagine a separate PEP800 style guide for regular code, but we don't need to do that, because in almost all cases PEP8 works great for regular code. I have taught thousands of new Python programmers, and the only place where PEP8 serves them poorly is this mandatory-is rule. Therefore instead of a separate style guide for regular code, I propose an exception for this one problem rule.
Ultimately this comes down to the question - should PEP8 push regular, not-Python-implementation code to use is for singletons in cases where == works perfectly? Seeing how effortless it is for programmers to use == as their first choice, I think PEP8 should allow that practice.
Best,
Nick _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at
https://mail.python.org/archives/list/python-ideas@python.org/message/JWLKBT...
Code of Conduct: http://python.org/psf/codeofconduct/
Are there other such classes?
from the top of my head, Pandas: In [13]: pd.DataFrame([[1,2], [3, 4]]) == None Out[13]: 0 1 0 False False 1 False False and any zarr container, or xarray will behave like numpy and broadcast. Ah, that means probably Dask, and Ray. Also maybe CuPy ? But I'm not going to list all that behave like numpy or pandas, but that's a big chunk of things that return non boolean values. Let's grep into my dev folder for other weird __eq__ $ rg 'def __eq__' -A3 ... stuff, let's pick a few: sympy.physics.optics.Medium: def __eq__(self, other): return self.refractive_index == other.refractive_index Will crash with None has no attribute refractive_index, IPython's internal Completions objects around Jedi also assume they are compared against instances of the same class and will crash, but likely with 'NoneType' object is not subscriptable. matplotlib FontProperties have a small but non-zero change of telling you that the current (object == None) is True as it compares hashes, so if the hash of the FontProp is the same as None... well. Basically anything that implements __eq__ and assumes it will be compared only against things that are of the same type will not be happy to be compared with None using ==. or implement broadcasting like abilities. -- Matthias On Tue, 31 Aug 2021 at 16:22, Nick Parlante <nick@cs.stanford.edu> wrote:
To argue that == is unreliable, I think Matthias has the best, non-contrived example with numpy, which I did not know about:
x = np.array([1, 2, 3]) x == None array([False, False, False])
This certainly is a good example of == not being reliable. Point taken there.
Are there other such classes? Like if I write code that just uses Python and its standard library classes, can I get a value where == None is flaky like the numpy example? I wouldn't think so, just as a matter of principle-of-least-surprise API design. Anyway, that would be very interesting example to argue against my claim that == None is reliable.
Best,
Nick
On Mon, Aug 30, 2021 at 12:06 PM Matthias Bussonnier <bussonniermatthias@gmail.com> wrote:
From my point of view as someone who sometimes help Scientist write Python, this is a no go, there are too many cases where == and is are different.
$ ipython Python 3.8.5 | packaged by conda-forge | (default, Sep 16 2020, 17:43:11) Type 'copyright', 'credits' or 'license' for more information IPython 7.25.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]: import numpy as np
In [2]: a = np.array([True, False, None])
In [3]: a == True, a == False, a == None Out[3]: (array([ True, False, False]), array([False, True, False]), array([False, False, True]))
In [4]: a is True, a is False, a is None Out[4]: (False, False, False)
if `a == True` can even raise errors when in ifs:
In [5]: if a == True: ...: pass ...: --------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-5-59a850ef5f8d> in <module> ----> 1 if a == True: 2 pass 3
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
Basic types are just too simple to expose the real need between `==` and `is`, but that's not a reason not to give the right advice from the start. IMHO It would be like teaching English and saying that it's ok not to put s after nouns when there is a number in front as it's obvious there are many.
I do understand your concern, but I believe that would be just pushing the problem to later, when it would be much more difficult to explain and have students get a wrong mental model from the start, which is really hard to overcome.
-- Matthias
On Mon, 30 Aug 2021 at 11:45, Nick Parlante <nick@cs.stanford.edu> wrote:
Hi there python-ideas - I've been teaching Python as a first programming language for a few years, and from that experience I want to propose a change to PEP8. I'm sure the default position for PEP8 is to avoid changing it. However, for this one rule I think a good case can be made to make it optional, so let me know what you think.
Let me start with what I've learned from teaching students in Java and now in Python. In Java, you use == for ints, but you need to use equals() for strings. Of course students screw this up constantly, using == in a context that calls for equals() and their code does not work right. Then for Java arrays a different comparison function is required, and so it goes. To teach comparisons in Python, I simply say "just use ==" - it works for ints, for strings, even for lists. Students are blown away by how nice and simple this is. This is how things should work. Python really gets this right.
So what is the problem?
The problem for Python is what I will call the "mandatory-is" rule in PEP8, which reads:
Comparisons to singletons like None should always be done with is or is not, never the equality operators.
For the students, this comes up in the first week of the course with lines like "if x == None:" which work perfectly with == but should use is/is-not for PEP8 conformance.
My guess is that this rule is in PEP8 because, within a Python implementation, it is within the programmer's mental model that, say, False is a singleton. The mandatory-is rule is in PEP8 to reinforce that mental model by requiring the is operator. Plus it probably runs a tiny bit faster.
However, for "regular" Python code, not implementing Python, forcing the use of is instead of the simpler == is unneeded and unhelpful (and analogously forcing "is not" when != works correctly). What is the benefit of forcing the is operator there? I would say it spreads an awareness of the details of how certain values are allocated within Python. That's not much of a benefit, and it's kind of circular. Like if programmers were permitted to use ==, they wouldn't need to know the details of how Python allocates those values. Being shielded from implementation details is a Python strength - think of the Java vs. Python story above. Is Java better because it builds an awareness in the programmer of the different comparison functions for different types? Of course not! Python is better in that case because it lets the programmer simply use == and not think about those details. Understanding the singleton strategy is important in some corners of coding, but forcing the is operator on all Python code is way out of proportion to the benefit.
As a practical matter, the way this comes up for my students is that IDEs by default will put warning marks around PEP8 violations in their code. Mostly this IDE-coaching is very helpful for students learning Python. For example, It's great that beginning Python programmers learn to put one space around operators right from the first day. Having taught thousands of introductory Python students, the one PEP8 rule that causes problems is this mandatory-is rule.
As a teacher, this is especially jarring since the "just use ==" rule is so effortless to use correctly. In contrast, the mandatory-is rule adds a little pause where the programmer should think about which comparison operator is the correct one to use. It's not hard, but it feels unnecessary.
As a contrasting example, in the language C, programmers need to understand == vs. is right from the first day. You can't get anything done in C without understanding that distinction. However that is just not true for regular (not-Python-implementation) Python code, where == works correctly for the great majority of cases.
Here is my proposal:
Add the following parenthetical to the mandatory-is rule: (this rule is optional for code that is not part of an implementation of Python).
So in effect, programmers outside of a Python implementation can choose to use == or is for the "if x == None:" case. In this way, PEP8 conforming code before the change is still conforming. Moving forward, I would expect that regular code will trend towards using == in such a case, reserving is for the rare cases where it is needed for correctness.
PEP8 was originally just for Python implementations, so why is this change needed? Because as a practical matter, the vast majority of code that is using PEP8 is not part of a Python implementation. This may not have been the original mission of PEP8, but it is how things have worked out.
Now we are in a situation where the rules in PEP8 are sent out to this ocean of Python programmers of many different ability levels writing regular code that is not a Python implementation. One could imagine a separate PEP800 style guide for regular code, but we don't need to do that, because in almost all cases PEP8 works great for regular code. I have taught thousands of new Python programmers, and the only place where PEP8 serves them poorly is this mandatory-is rule. Therefore instead of a separate style guide for regular code, I propose an exception for this one problem rule.
Ultimately this comes down to the question - should PEP8 push regular, not-Python-implementation code to use is for singletons in cases where == works perfectly? Seeing how effortless it is for programmers to use == as their first choice, I think PEP8 should allow that practice.
Best,
Nick _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/JWLKBT... Code of Conduct: http://python.org/psf/codeofconduct/
On Wed, 1 Sept 2021 at 01:20, Matthias Bussonnier < bussonniermatthias@gmail.com> wrote:
Are there other such classes?
[snip]
... stuff, let's pick a few:
sympy.physics.optics.Medium:
def __eq__(self, other): return self.refractive_index == other.refractive_index
Will crash with None has no attribute refractive_index,
I think that particular __eq__ method is buggy. There should be a general expectation that __eq__ can be used with unexpected types and should return NotImplemented or False rather than raise an exception. Oscar
Wow Matthias, you are like the pied piper of weird == implementations! I'm glad you were able to put those in the discussion. To check into Python and its standard modules itself ... I can imagine putting together a grep type thing to just dig all the __eq__ out of there and look at them. Like how many are there I wonder? I suppose the tend to be implemented on the C side anyway. Best, Nick On Tue, Aug 31, 2021 at 5:17 PM Matthias Bussonnier < bussonniermatthias@gmail.com> wrote:
Are there other such classes?
from the top of my head, Pandas:
In [13]: pd.DataFrame([[1,2], [3, 4]]) == None Out[13]: 0 1 0 False False 1 False False
and any zarr container, or xarray will behave like numpy and broadcast. Ah, that means probably Dask, and Ray. Also maybe CuPy ? But I'm not going to list all that behave like numpy or pandas, but that's a big chunk of things that return non boolean values.
Let's grep into my dev folder for other weird __eq__
$ rg 'def __eq__' -A3
... stuff, let's pick a few:
sympy.physics.optics.Medium:
def __eq__(self, other): return self.refractive_index == other.refractive_index
Will crash with None has no attribute refractive_index,
IPython's internal Completions objects around Jedi also assume they are compared against instances of the same class and will crash, but likely with 'NoneType' object is not subscriptable.
matplotlib FontProperties have a small but non-zero change of telling you that the current (object == None) is True as it compares hashes, so if the hash of the FontProp is the same as None... well.
Basically anything that implements __eq__ and assumes it will be compared only against things that are of the same type will not be happy to be compared with None using ==. or implement broadcasting like abilities. -- Matthias
On Tue, 31 Aug 2021 at 16:22, Nick Parlante <nick@cs.stanford.edu> wrote:
To argue that == is unreliable, I think Matthias has the best,
non-contrived example with numpy, which I did not know about:
x = np.array([1, 2, 3]) x == None array([False, False, False])
This certainly is a good example of == not being reliable. Point taken
there.
Are there other such classes? Like if I write code that just uses Python
and its standard library classes, can I get a value where == None is flaky like the numpy example? I wouldn't think so, just as a matter of principle-of-least-surprise API design. Anyway, that would be very interesting example to argue against my claim that == None is reliable.
Best,
Nick
On Mon, Aug 30, 2021 at 12:06 PM Matthias Bussonnier <
bussonniermatthias@gmail.com> wrote:
From my point of view as someone who sometimes help Scientist write Python, this is a no go, there are too many cases where == and is are different.
$ ipython Python 3.8.5 | packaged by conda-forge | (default, Sep 16 2020,
17:43:11)
Type 'copyright', 'credits' or 'license' for more information IPython 7.25.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]: import numpy as np
In [2]: a = np.array([True, False, None])
In [3]: a == True, a == False, a == None Out[3]: (array([ True, False, False]), array([False, True, False]), array([False, False, True]))
In [4]: a is True, a is False, a is None Out[4]: (False, False, False)
if `a == True` can even raise errors when in ifs:
In [5]: if a == True: ...: pass ...:
ValueError Traceback (most recent call last) <ipython-input-5-59a850ef5f8d> in <module> ----> 1 if a == True: 2 pass 3
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
Basic types are just too simple to expose the real need between `==` and `is`, but that's not a reason not to give the right advice from the start. IMHO It would be like teaching English and saying that it's ok not to put s after nouns when there is a number in front as it's obvious there are many.
I do understand your concern, but I believe that would be just pushing the problem to later, when it would be much more difficult to explain and have students get a wrong mental model from the start, which is really hard to overcome.
-- Matthias
On Mon, 30 Aug 2021 at 11:45, Nick Parlante <nick@cs.stanford.edu> wrote:
Hi there python-ideas - I've been teaching Python as a first programming language for a few years, and from that experience I want to propose a change to PEP8. I'm sure the default position for PEP8 is to avoid changing it. However, for this one rule I think a good case can be made to make it optional, so let me know what you think.
Let me start with what I've learned from teaching students in Java and now in Python. In Java, you use == for ints, but you need to use equals() for strings. Of course students screw this up constantly, using == in a context that calls for equals() and their code does not work right. Then for Java arrays a different comparison function is required, and so it goes. To teach comparisons in Python, I simply say "just use ==" - it works for ints, for strings, even for lists. Students are blown away by how nice and simple this is. This is how things should work. Python really gets this right.
So what is the problem?
The problem for Python is what I will call the "mandatory-is" rule in PEP8, which reads:
Comparisons to singletons like None should always be done with is or is not, never the equality operators.
For the students, this comes up in the first week of the course with lines like "if x == None:" which work perfectly with == but should use is/is-not for PEP8 conformance.
My guess is that this rule is in PEP8 because, within a Python implementation, it is within the programmer's mental model that, say, False is a singleton. The mandatory-is rule is in PEP8 to reinforce that mental model by requiring the is operator. Plus it probably runs a tiny bit faster.
However, for "regular" Python code, not implementing Python, forcing the use of is instead of the simpler == is unneeded and unhelpful (and analogously forcing "is not" when != works correctly). What is the benefit of forcing the is operator there? I would say it spreads an awareness of the details of how certain values are allocated within Python. That's not much of a benefit, and it's kind of circular. Like if programmers were permitted to use ==, they wouldn't need to know the details of how Python allocates those values. Being shielded from implementation details is a Python strength - think of the Java vs. Python story above. Is Java better because it builds an awareness in the programmer of the different comparison functions for different types? Of course not! Python is better in that case because it lets the programmer simply use == and not think about those details. Understanding the singleton strategy is important in some corners of coding, but forcing the is operator on all Python code is way out of proportion to the benefit.
As a practical matter, the way this comes up for my students is that IDEs by default will put warning marks around PEP8 violations in their code. Mostly this IDE-coaching is very helpful for students learning Python. For example, It's great that beginning Python programmers learn to put one space around operators right from the first day. Having taught thousands of introductory Python students, the one PEP8 rule that causes problems is this mandatory-is rule.
As a teacher, this is especially jarring since the "just use ==" rule is so effortless to use correctly. In contrast, the mandatory-is rule adds a little pause where the programmer should think about which comparison operator is the correct one to use. It's not hard, but it feels unnecessary.
As a contrasting example, in the language C, programmers need to understand == vs. is right from the first day. You can't get anything done in C without understanding that distinction. However that is just not true for regular (not-Python-implementation) Python code, where == works correctly for the great majority of cases.
Here is my proposal:
Add the following parenthetical to the mandatory-is rule: (this rule is optional for code that is not part of an implementation of Python).
So in effect, programmers outside of a Python implementation can choose to use == or is for the "if x == None:" case. In this way, PEP8 conforming code before the change is still conforming. Moving forward, I would expect that regular code will trend towards using == in such a case, reserving is for the rare cases where it is needed for correctness.
PEP8 was originally just for Python implementations, so why is this change needed? Because as a practical matter, the vast majority of code that is using PEP8 is not part of a Python implementation. This may not have been the original mission of PEP8, but it is how things have worked out.
Now we are in a situation where the rules in PEP8 are sent out to this ocean of Python programmers of many different ability levels writing regular code that is not a Python implementation. One could imagine a separate PEP800 style guide for regular code, but we don't need to do that, because in almost all cases PEP8 works great for regular code. I have taught thousands of new Python programmers, and the only place where PEP8 serves them poorly is this mandatory-is rule. Therefore instead of a separate style guide for regular code, I propose an exception for this one problem rule.
Ultimately this comes down to the question - should PEP8 push regular, not-Python-implementation code to use is for singletons in cases where == works perfectly? Seeing how effortless it is for programmers to use == as their first choice, I think PEP8 should allow that practice.
Best,
Nick _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at
https://mail.python.org/archives/list/python-ideas@python.org/message/JWLKBT...
Code of Conduct: http://python.org/psf/codeofconduct/
On Tue, Aug 31, 2021 at 05:17:35PM -0700, Matthias Bussonnier wrote:
Basically anything that implements __eq__ and assumes it will be compared only against things that are of the same type will not be happy to be compared with None using ==.
I agree with Oscar that the various objects you list that fail when comparing against other objects are buggy. All operators, including comparisons, are supposed to return NotImplemented if called with an argument they don't know how to operate on. And as for FontProperties comparing hashes only, ouch! Hopefully its a 64-bit hash and not a 32- or 16-bit. -- Steve
On Tue, Aug 31, 2021 at 4:46 AM Nick Parlante <nick@cs.stanford.edu> wrote:
The problem for Python is what I will call the "mandatory-is" rule in PEP8, which reads:
Comparisons to singletons like None should always be done with is or is not, never the equality operators.
The question is whether you're testing whether some value IS something else, or whether it IS EQUAL TO, and that's an important one. If x IS None, then it cannot possibly be anything else; but it can be EQUAL TO a number while being a different form of it. For instance:
5 == 5.0 True
But there will never be any other None. There cannot ever be another True or False either, nor another Ellipsis. If you're asking about those, you're not asking "is this equal to None?", you are asking "is this thing the special value None?". Since those are different questions, the distinction needs to remain. However!
For the students, this comes up in the first week of the course with lines like "if x == None:" which work perfectly with == but should use is/is-not for PEP8 conformance.
This isn't a major problem. When you're just teaching something for the first time, you don't have to worry about that distinction. I don't see any issue with teaching them "just use ==" initially, and introducing the importance of object identity later (maybe alongside something about mutable objects).
However, for "regular" Python code, not implementing Python, forcing the use of is instead of the simpler == is unneeded and unhelpful (and analogously forcing "is not" when != works correctly). What is the benefit of forcing the is operator there? I would say it spreads an awareness of the details of how certain values are allocated within Python.
It's not about implementation (although you're right that identity checks can be faster); the two operators ask different questions.
As a practical matter, the way this comes up for my students is that IDEs by default will put warning marks around PEP8 violations in their code. Mostly this IDE-coaching is very helpful for students learning Python. For example, It's great that beginning Python programmers learn to put one space around operators right from the first day. Having taught thousands of introductory Python students, the one PEP8 rule that causes problems is this mandatory-is rule.
That's a good reason to reconfigure your IDE. If it's set up in a way that is bad for teaching, change your IDE configs. (Ideally, that's a simple matter of providing a JSON or YAML file and saying "save this into this location". The students don't need to understand what that file is saying, but it will solve their issues.)
Here is my proposal:
Add the following parenthetical to the mandatory-is rule: (this rule is optional for code that is not part of an implementation of Python).
https://www.python.org/dev/peps/pep-0008/#introduction "Many projects have their own coding style guidelines. In the event of any conflicts, such project-specific guides take precedence for that project." EVERYTHING in that document is optional for code that isn't part of the Python standard library.
So in effect, programmers outside of a Python implementation can choose to use == or is for the "if x == None:" case. In this way, PEP8 conforming code before the change is still conforming. Moving forward, I would expect that regular code will trend towards using == in such a case, reserving is for the rare cases where it is needed for correctness.
I disagree; experienced programmers should be using "is" where it's correct. But students who are freshly learning the language are welcome to do otherwise, just as they're very likely to violate other style recommendations. It's not a problem. (For instance, I frequently see people put spaces around equals signs in keyword args. That's a violation of PEP 8, but if it's not part of the standard library, no big deal.)
Now we are in a situation where the rules in PEP8 are sent out to this ocean of Python programmers of many different ability levels writing regular code that is not a Python implementation. One could imagine a separate PEP800 style guide for regular code, but we don't need to do that, because in almost all cases PEP8 works great for regular code. I have taught thousands of new Python programmers, and the only place where PEP8 serves them poorly is this mandatory-is rule. Therefore instead of a separate style guide for regular code, I propose an exception for this one problem rule.
Make your own style guide. Enforce it to the exact extent that it matters for you. You can even say "follow PEP 8 where it does not conflict with this guide" to take advantage of the parts you like, while still having all the freedom you need. PEP 8 is not, and never has been, a set of mandatory rules for all Python programmers. Nobody will arrest you for violating it, least of all the Python Secret Underground, which emphatically does not exi[NO CARRIER ChrisA
Hi Chris - thanks for the response, so here you say: I disagree; experienced programmers should be using "is" where it's
correct.
I'd like to unpack that a little - like what do you mean by "correct" there. My guess is "correct" means the code gets the right answer for all inputs. Like for "if x == None:" works right By that definition, == is correct there. I think PEP8 has forced people to use "is" so much, they've lost touch with the reality that in fact == would work perfectly in almost all cases. Situations where "is" is required for correctness are very rare. The argument for mandatory-is is not based on correctness - PEP8 doesn't say to use is where it's needed. What's key here is that PEP8 is forcing "is" into situations where it is *not needed*. So the justification for mandatory-is needs to work more on esthetics - like "is" is not required there for correctness, but we want to require it because it reinforces in the code how certain values are allocated, plus as mentioned I think it runs a little faster. Is this esthetic so great that we don't permit the simpler practice of using == where it works fine? Best, Nick PEP8 mandatory-is is not using that standard - it's forcing "is" in a kind of decorative way, I suspect to reinforce that None is allocated once, but the number 2000 On Mon, Aug 30, 2021 at 12:09 PM Chris Angelico <rosuav@gmail.com> wrote:
On Tue, Aug 31, 2021 at 4:46 AM Nick Parlante <nick@cs.stanford.edu> wrote:
The problem for Python is what I will call the "mandatory-is" rule in PEP8, which reads:
Comparisons to singletons like None should always be done with is or is not, never the equality operators.
The question is whether you're testing whether some value IS something else, or whether it IS EQUAL TO, and that's an important one. If x IS None, then it cannot possibly be anything else; but it can be EQUAL TO a number while being a different form of it. For instance:
5 == 5.0 True
But there will never be any other None. There cannot ever be another True or False either, nor another Ellipsis. If you're asking about those, you're not asking "is this equal to None?", you are asking "is this thing the special value None?". Since those are different questions, the distinction needs to remain.
However!
For the students, this comes up in the first week of the course with lines like "if x == None:" which work perfectly with == but should use is/is-not for PEP8 conformance.
This isn't a major problem. When you're just teaching something for the first time, you don't have to worry about that distinction. I don't see any issue with teaching them "just use ==" initially, and introducing the importance of object identity later (maybe alongside something about mutable objects).
However, for "regular" Python code, not implementing Python, forcing the use of is instead of the simpler == is unneeded and unhelpful (and analogously forcing "is not" when != works correctly). What is the benefit of forcing the is operator there? I would say it spreads an awareness of the details of how certain values are allocated within Python.
It's not about implementation (although you're right that identity checks can be faster); the two operators ask different questions.
As a practical matter, the way this comes up for my students is that IDEs by default will put warning marks around PEP8 violations in their code. Mostly this IDE-coaching is very helpful for students learning Python. For example, It's great that beginning Python programmers learn to put one space around operators right from the first day. Having taught thousands of introductory Python students, the one PEP8 rule that causes problems is this mandatory-is rule.
That's a good reason to reconfigure your IDE. If it's set up in a way that is bad for teaching, change your IDE configs. (Ideally, that's a simple matter of providing a JSON or YAML file and saying "save this into this location". The students don't need to understand what that file is saying, but it will solve their issues.)
Here is my proposal:
Add the following parenthetical to the mandatory-is rule: (this rule is optional for code that is not part of an implementation of Python).
https://www.python.org/dev/peps/pep-0008/#introduction "Many projects have their own coding style guidelines. In the event of any conflicts, such project-specific guides take precedence for that project."
EVERYTHING in that document is optional for code that isn't part of the Python standard library.
So in effect, programmers outside of a Python implementation can choose to use == or is for the "if x == None:" case. In this way, PEP8 conforming code before the change is still conforming. Moving forward, I would expect that regular code will trend towards using == in such a case, reserving is for the rare cases where it is needed for correctness.
I disagree; experienced programmers should be using "is" where it's correct. But students who are freshly learning the language are welcome to do otherwise, just as they're very likely to violate other style recommendations. It's not a problem. (For instance, I frequently see people put spaces around equals signs in keyword args. That's a violation of PEP 8, but if it's not part of the standard library, no big deal.)
Now we are in a situation where the rules in PEP8 are sent out to this ocean of Python programmers of many different ability levels writing regular code that is not a Python implementation. One could imagine a separate PEP800 style guide for regular code, but we don't need to do that, because in almost all cases PEP8 works great for regular code. I have taught thousands of new Python programmers, and the only place where PEP8 serves them poorly is this mandatory-is rule. Therefore instead of a separate style guide for regular code, I propose an exception for this one problem rule.
Make your own style guide. Enforce it to the exact extent that it matters for you. You can even say "follow PEP 8 where it does not conflict with this guide" to take advantage of the parts you like, while still having all the freedom you need.
PEP 8 is not, and never has been, a set of mandatory rules for all Python programmers. Nobody will arrest you for violating it, least of all the Python Secret Underground, which emphatically does not exi[NO CARRIER
ChrisA _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/5QPN2V... Code of Conduct: http://python.org/psf/codeofconduct/
On Tue, Aug 31, 2021 at 5:34 AM Nick Parlante <nick@cs.stanford.edu> wrote:
Hi Chris - thanks for the response,
so here you say:
I disagree; experienced programmers should be using "is" where it's correct.
I'd like to unpack that a little - like what do you mean by "correct" there. My guess is "correct" means the code gets the right answer for all inputs.
Like for "if x == None:" works right
By that definition, == is correct there. I think PEP8 has forced people to use "is" so much, they've lost touch with the reality that in fact == would work perfectly in almost all cases. Situations where "is" is required for correctness are very rare.
class X: def __eq__(self, other): return True Now your code is broken, because it will accept an instance of X when it should only be accepting None. It's also broken if you use numpy:
if numpy.array([1,2,3]) == None: pass ... Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
So, no, == is not correct. (Which reads weirdly, but it's still True, since correct is not the equals-equals operator.) These distinctions probably don't matter to a novice, but experienced programmers should know (or be able to figure out) whether they're looking for an identity check or an equality check. There are a few rules of thumb to help: 1) If you're comparing numbers or strings (Unicode or byte), use equality, because there could be multiple that have equivalent values. (Easiest to demonstrate with numbers since 5 == 5.0 == Fraction(5) == Decimal("5") but also true of strings in CPython.) 2) If you're checking for sentinel objects (None, Ellipsis, etc), use is. 3) Otherwise, use == unless you know you're doing an identity check. That'll cover most situations. I wouldn't bother teaching the distinction until you're looking at mutable objects (for instance, [] == [] because they currently have the same value, but if you append something to one of those lists, they won't; and you can use 'is' to figure out whether two things are actually the same list). ChrisA
On 2021-08-30 12:34, Nick Parlante wrote:
Like for "if x == None:" works right
By that definition, == is correct there. I think PEP8 has forced people to use "is" so much, they've lost touch with the reality that in fact == would work perfectly in almost all cases. Situations where "is" is required for correctness are very rare.
But you can't know if `x == None` is correct without knowing what `x` is. For instance, as someone mentioned earlier in this thread ,if `x` is a numpy array, `x == None` will return another numpy array, and trying to use that in an `if` will give an error. The basic issue is that objects can customize what `==` does, but they can't customize what `is` does. That means that `is` is "safer" in the sense that you always know it's going to do what it always does, regardless of the types of the objects you're comparing. I agree with Chris though that it sounds like your issue is really one of teaching rather than one of PEP8. It's fine to not teach students how to do everything according to PEP8 right from the get-go. Especially for something like `is None`, it's unlikely to be a huge deal for students to later learn the "correct" (aka PEP8-endorsed) way, once they have a more nuanced understanding of how comparisons work in Python. There are all kinds of recommendations in PEP8 that aren't worth worrying about at the initial stages of learning Python. -- Brendan Barnwell "Do not follow where the path may lead. Go, instead, where there is no path, and leave a trail." --author unknown
On Mon, Aug 30, 2021, 15:38 Nick Parlante <nick@cs.stanford.edu> wrote:
Hi Chris - thanks for the response,
so here you say:
I disagree; experienced programmers should be using "is" where it's
correct.
I think PEP8 has forced people to use "is" so much, they've lost touch with the reality that in fact == would work perfectly in almost all cases. Situations where "is" is required for correctness are very rare.
For you. For me, x==None failing is by far the most common scenario. That is because I do a lot of numeric programming and the only place I am likely to test for None is when setting a default input for a mutable value, which is an array-like most of the time. And numeric programming is one of the most common python use-cases so it isn't like that can just be ignored. And it isn't just numeric programming. There is an enormous number of different ways == is modified. But you can't change "is" in any reasonably simple way. The great thing about python duck-typing is that you often don't need to care about what your inputs are. But it also means you shouldn't make your code needlessly brittle so it only works properly with a handful of stdlib types.
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() On Mon, Aug 30, 2021 at 11:32 AM Nick Parlante <nick@cs.stanford.edu> wrote:
Hi there python-ideas - I've been teaching Python as a first programming language for a few years, and from that experience I want to propose a change to PEP8. I'm sure the default position for PEP8 is to avoid changing it. However, for this one rule I think a good case can be made to make it optional, so let me know what you think.
Let me start with what I've learned from teaching students in Java and now in Python. In Java, you use == for ints, but you need to use equals() for strings. Of course students screw this up constantly, using == in a context that calls for equals() and their code does not work right. Then for Java arrays a different comparison function is required, and so it goes. To teach comparisons in Python, I simply say "just use ==" - it works for ints, for strings, even for lists. Students are blown away by how nice and simple this is. This is how things should work. Python really gets this right.
So what is the problem?
The problem for Python is what I will call the "mandatory-is" rule in PEP8, which reads:
Comparisons to singletons like None should always be done with is or is not, never the equality operators.
For the students, this comes up in the first week of the course with lines like "if x == None:" which work perfectly with == but should use is/is-not for PEP8 conformance.
My guess is that this rule is in PEP8 because, within a Python implementation, it is within the programmer's mental model that, say, False is a singleton. The mandatory-is rule is in PEP8 to reinforce that mental model by requiring the is operator. Plus it probably runs a tiny bit faster.
However, for "regular" Python code, not implementing Python, forcing the use of is instead of the simpler == is unneeded and unhelpful (and analogously forcing "is not" when != works correctly). What is the benefit of forcing the is operator there? I would say it spreads an awareness of the details of how certain values are allocated within Python. That's not much of a benefit, and it's kind of circular. Like if programmers were permitted to use ==, they wouldn't need to know the details of how Python allocates those values. Being shielded from implementation details is a Python strength - think of the Java vs. Python story above. Is Java better because it builds an awareness in the programmer of the different comparison functions for different types? Of course not! Python is better in that case because it lets the programmer simply use == and not think about those details. Understanding the singleton strategy is important in some corners of coding, but forcing the is operator on all Python code is way out of proportion to the benefit.
As a practical matter, the way this comes up for my students is that IDEs by default will put warning marks around PEP8 violations in their code. Mostly this IDE-coaching is very helpful for students learning Python. For example, It's great that beginning Python programmers learn to put one space around operators right from the first day. Having taught thousands of introductory Python students, the one PEP8 rule that causes problems is this mandatory-is rule.
As a teacher, this is especially jarring since the "just use ==" rule is so effortless to use correctly. In contrast, the mandatory-is rule adds a little pause where the programmer should think about which comparison operator is the correct one to use. It's not hard, but it feels unnecessary.
As a contrasting example, in the language C, programmers need to understand == vs. is right from the first day. You can't get anything done in C without understanding that distinction. However that is just not true for regular (not-Python-implementation) Python code, where == works correctly for the great majority of cases.
Here is my proposal:
Add the following parenthetical to the mandatory-is rule: (this rule is optional for code that is not part of an implementation of Python).
So in effect, programmers outside of a Python implementation can choose to use == or is for the "if x == None:" case. In this way, PEP8 conforming code before the change is still conforming. Moving forward, I would expect that regular code will trend towards using == in such a case, reserving is for the rare cases where it is needed for correctness.
PEP8 was originally just for Python implementations, so why is this change needed? Because as a practical matter, the vast majority of code that is using PEP8 is not part of a Python implementation. This may not have been the original mission of PEP8, but it is how things have worked out.
Now we are in a situation where the rules in PEP8 are sent out to this ocean of Python programmers of many different ability levels writing regular code that is not a Python implementation. One could imagine a separate PEP800 style guide for regular code, but we don't need to do that, because in almost all cases PEP8 works great for regular code. I have taught thousands of new Python programmers, and the only place where PEP8 serves them poorly is this mandatory-is rule. Therefore instead of a separate style guide for regular code, I propose an exception for this one problem rule.
Ultimately this comes down to the question - should PEP8 push regular, not-Python-implementation code to use is for singletons in cases where == works perfectly? Seeing how effortless it is for programmers to use == as their first choice, I think PEP8 should allow that practice.
Best,
Nick
On Tue, Aug 31, 2021 at 6:52 AM Nick Parlante <nick@cs.stanford.edu> wrote:
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.
On what basis do you ascertain whether "==" would work correctly? Please explain. ChrisA
On what basis do you ascertain whether "==" would work correctly? Please explain.
Hi Chris, I'm just glancing at the line of code, and doing a little thought experiment to see if it would get the same output if == was used instead. For a singleton like None or False or the class like "list" .. == will return the same answer as "is". Look at these lines; if mode is None: if type(items) is list: If that code works with "is" it's going to work with == too. People are not used to seeing == in these cases, but it works:
x = None x is None True x == None True
t = type([1, 2, 3]) t is list True t == list True
fn = list.index fn is list.index True fn == list.index True
The situations where "is" is truly needed are rather esoteric. Best, Nick On Mon, Aug 30, 2021 at 2:02 PM Chris Angelico <rosuav@gmail.com> wrote:
On Tue, Aug 31, 2021 at 6:52 AM Nick Parlante <nick@cs.stanford.edu> wrote:
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.
On what basis do you ascertain whether "==" would work correctly? Please explain.
ChrisA _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/5LM3UM... Code of Conduct: http://python.org/psf/codeofconduct/
On Tue, Aug 31, 2021 at 7:17 AM Nick Parlante <nick@cs.stanford.edu> wrote:
On what basis do you ascertain whether "==" would work correctly? Please explain.
Hi Chris, I'm just glancing at the line of code, and doing a little thought experiment to see if it would get the same output if == was used instead. For a singleton like None or False or the class like "list" .. == will return the same answer as "is". Look at these lines;
if mode is None: if type(items) is list:
If that code works with "is" it's going to work with == too. People are not used to seeing == in these cases, but it works:
x = None x is None True x == None True
t = type([1, 2, 3]) t is list True t == list True
fn = list.index fn is list.index True fn == list.index True
The situations where "is" is truly needed are rather esoteric.
class X: ... def __eq__(self, other): return True ... x = X() x is None False x == None True type([1, 2, 3]) is x False type([1, 2, 3]) == x True x is list.index False x == list.index True
Revisit your assumptions :) ChrisA
On Tue, Aug 31, 2021 at 7:22 AM Chris Angelico <rosuav@gmail.com> wrote:
On Tue, Aug 31, 2021 at 7:17 AM Nick Parlante <nick@cs.stanford.edu> wrote:
On what basis do you ascertain whether "==" would work correctly? Please explain.
Hi Chris, I'm just glancing at the line of code, and doing a little thought experiment to see if it would get the same output if == was used instead. For a singleton like None or False or the class like "list" .. == will return the same answer as "is". Look at these lines;
if type(items) is list:
Oh, and this one is a little more work to prove, but it can be done.
class Meta(type): ... def __eq__(self, other): return True ... class List(list, metaclass=Meta): pass ... items = List([1, 2, 3]) items [1, 2, 3] type(items) is list False type(items) == list True
(If you prefer, you could have items be a dict-like type instead of list-like, so it compares equal to list but doesn't behave like one. The demonstration is identical.) ChrisA
class X: ... def __eq__(self, other): return True ... x = X() x is None False x == None True
I don't know Chris, doesn't this just show that If you construct a class with a, shall we say, "oddball" definition of ==, then using == on that class gets oddball results. And it goes without saying that we all understand that None and False and True (the dominant cases where PEP8 is forcing "is") will always have sensible definitions of ==. I'm not saying == has to be used for everything or that it's not possible to construct a case where == is inappropriate . I'm saying PEP8 should permit using == where it works correctly. That's it. The cases where == works correctly are incredibly common. If we has a class, say, where == has some weird behavior, the solution is that the docs for that class explain how its definition of == is weird and so code should take care to use "is" or whatever. That's kind of canonical use of Docs, right? Like describe the way in which this class does not act as you might expect. The solution is not to prohibit using == on the vast number of nice, regular classes where == works as expected. Best, Nick On Mon, Aug 30, 2021 at 2:24 PM Chris Angelico <rosuav@gmail.com> wrote:
On what basis do you ascertain whether "==" would work correctly? Please explain.
Hi Chris, I'm just glancing at the line of code, and doing a little
On Tue, Aug 31, 2021 at 7:17 AM Nick Parlante <nick@cs.stanford.edu> wrote: thought experiment to see if it would get the same output if == was used instead. For a singleton like None or False or the class like "list" .. == will return the same answer as "is". Look at these lines;
if mode is None: if type(items) is list:
If that code works with "is" it's going to work with == too. People are
not used to seeing == in these cases, but it works:
x = None x is None True x == None True
t = type([1, 2, 3]) t is list True t == list True
fn = list.index fn is list.index True fn == list.index True
The situations where "is" is truly needed are rather esoteric.
class X: ... def __eq__(self, other): return True ... x = X() x is None False x == None True type([1, 2, 3]) is x False type([1, 2, 3]) == x True x is list.index False x == list.index True
Revisit your assumptions :)
ChrisA _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/AA6IIC... Code of Conduct: http://python.org/psf/codeofconduct/
On Mon, 30 Aug 2021 at 23:27, Nick Parlante <nick@cs.stanford.edu> wrote:
I don't know Chris, doesn't this just show that If you construct a class with a, shall we say, "oddball" definition of ==, then using == on that class gets oddball results. And it goes without saying that we all understand that None and False and True (the dominant cases where PEP8 is forcing "is") will always have sensible definitions of ==.
If you consider numpy arrays as "oddball" and unworthy of consideration, then I guess you're correct. But PEP 8 isn't going to change because you dismiss a major Python library as irrelevant... And as everyone has already said, PEP 8 isn't *forcing* anyone to do anything. If you don't like it, ignore it. But don't try to insist everyone else has to agree with you. Paul
Hi Nick, On Mon, Aug 30, 2021 at 4:25 PM Nick Parlante <nick@cs.stanford.edu> wrote:
I don't know Chris, doesn't this just show that If you construct a class with a, shall we say, "oddball" definition of ==, then using == on that class gets oddball results. And it goes without saying that we all understand that None and False and True (the dominant cases where PEP8 is forcing "is") will always have sensible definitions of ==.
If you have `if x == None:`, you don't only have to care about whether `None` has an "oddball" definition of ==, you also have to care about whether the type of `x` does! Since in many of these cases `x` is something passed in from outside the function, you may not have any idea about or control over its type. That's why people are telling you that almost all the occurrences you've found would in fact be unsafe if changed from `is` to `==`, and it's very rare that you can "eyeball" a particular case and declare confidently that it would be fine to switch it from `is` to `==`. Carl
I understand that Python itself should work with classes with any sort of crazy semantics, and I appreciate your spelling it out. We could say that the bar for the Python implementation is the highest, since who knows what experimental == someone might cook up playing around, and you would like the underlying Python layers to hold up correctly no matter what. Now I chose the lines from the Python modules as just a random source of PEP8 compliant code, but I think I confused my message there, which was about "regular" not Python implementation code. Here is my proposal:
Add the following parenthetical to the mandatory-is rule: (this rule is optional for code that is not part of an implementation of Python).
So if I have a line like this if x == None: and this is in a Python program that is using regular old Python classes like int and list and dict and whatever. It is not importing a class with weird == semantics. We would agree, that == is going to work perfectly in that case. My code is relying on the == of the classes it uses, and that is sensible. Or perhaps my proposal should have been "It's permissible to use == in cases where it works correctly." Is PEP8 well served to forbid using == in cases where it works perfectly? Best, Nick On Mon, Aug 30, 2021 at 3:42 PM Carl Meyer <carl@oddbird.net> wrote:
Hi Nick,
On Mon, Aug 30, 2021 at 4:25 PM Nick Parlante <nick@cs.stanford.edu> wrote:
I don't know Chris, doesn't this just show that If you construct a class with a, shall we say, "oddball" definition of ==, then using == on that class gets oddball results. And it goes without saying that we all understand that None and False and True (the dominant cases where PEP8 is forcing "is") will always have sensible definitions of ==.
If you have `if x == None:`, you don't only have to care about whether `None` has an "oddball" definition of ==, you also have to care about whether the type of `x` does! Since in many of these cases `x` is something passed in from outside the function, you may not have any idea about or control over its type. That's why people are telling you that almost all the occurrences you've found would in fact be unsafe if changed from `is` to `==`, and it's very rare that you can "eyeball" a particular case and declare confidently that it would be fine to switch it from `is` to `==`.
Carl
On Tue, Aug 31, 2021 at 10:25 AM Nick Parlante <nick@cs.stanford.edu> wrote:
I understand that Python itself should work with classes with any sort of crazy semantics, and I appreciate your spelling it out. We could say that the bar for the Python implementation is the highest, since who knows what experimental == someone might cook up playing around, and you would like the underlying Python layers to hold up correctly no matter what.
Now I chose the lines from the Python modules as just a random source of PEP8 compliant code, but I think I confused my message there, which was about "regular" not Python implementation code.
Here is my proposal:
Add the following parenthetical to the mandatory-is rule: (this rule is optional for code that is not part of an implementation of Python).
So if I have a line like this
if x == None:
and this is in a Python program that is using regular old Python classes like int and list and dict and whatever. It is not importing a class with weird == semantics. We would agree, that == is going to work perfectly in that case. My code is relying on the == of the classes it uses, and that is sensible.
You don't seem to understand the full significance of "function parameter", then. Any time you're accepting a parameter from someone else, it COULD BE something like this. You can't know that it is a "regular old Python class" or anything else. Case in point: one of your examples, ast.py, first instance of " is " in the code: def parse(source, filename='<unknown>', mode='exec', *, type_comments=False, feature_version=None): ... if isinstance(feature_version, tuple): major, minor = feature_version # Should be a 2-tuple. assert major == 3 feature_version = minor elif feature_version is None: feature_version = -1 If you "import ast; ast.parse(...)", you can pass it *any Python object* as feature_version. ANY. It checks if it's a tuple (or a subclass thereof), and then, if it isn't that, checks to see if it is precisely the sentinel None. (I'm not sure why the checks are in that order. Given that the default argument is None, it would seem likely that the bulk of calls will have None, so I personally would have checked for None first and tuple second.) Might be time that this moves off python-ideas. I don't think PEP 8 is going to change to encourage a buggy practice. ChrisA
On Mon, Aug 30, 2021 at 03:24:08PM -0700, Nick Parlante wrote:
If we has a class, say, where == has some weird behavior, the solution is that the docs for that class explain how its definition of == is weird and so code should take care to use "is" or whatever. That's kind of canonical use of Docs, right?
You know that people don't read documentation until all else fails? And sometimes not even then. And that goes even more so for beginners. -- Steve
On Mon, Aug 30, 2021 at 1:50 PM Nick Parlante <nick@cs.stanford.edu> wrote:
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.
.. snip... BTW, it might be neat if someone could keep going through the Python module code and find a case where "is" really is needed. That must be in there somewhere, I'm just showing that at best, it's very rare. Best, Nick
EVERY line you found would behave incorrectly if changed to "x == None". Admittedly, doing so would require setting the various variables and attributes to somewhat uncommon objects that act "funny" when comparing to None. Or when doing equality comparisons in general. As others have said, learning the difference between equality and Identity is relevant pretty early in learning to program. No, not in the first day. Probably not on the second. But PEP 8 shouldn't be distorted just for people in their first month of programming... who are unlikely to be contributors to CPython so soon, as well. As soon as you teach the difference between: a, b = [], [] and: a = b = [] That distinction has become very important. Knowing operators to express that distinction is thereby important. On Mon, Aug 30, 2021, 4:51 PM Nick Parlante <nick@cs.stanford.edu> wrote:
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()
On Mon, Aug 30, 2021 at 11:32 AM Nick Parlante <nick@cs.stanford.edu> wrote:
Hi there python-ideas - I've been teaching Python as a first programming language for a few years, and from that experience I want to propose a change to PEP8. I'm sure the default position for PEP8 is to avoid changing it. However, for this one rule I think a good case can be made to make it optional, so let me know what you think.
Let me start with what I've learned from teaching students in Java and now in Python. In Java, you use == for ints, but you need to use equals() for strings. Of course students screw this up constantly, using == in a context that calls for equals() and their code does not work right. Then for Java arrays a different comparison function is required, and so it goes. To teach comparisons in Python, I simply say "just use ==" - it works for ints, for strings, even for lists. Students are blown away by how nice and simple this is. This is how things should work. Python really gets this right.
So what is the problem?
The problem for Python is what I will call the "mandatory-is" rule in PEP8, which reads:
Comparisons to singletons like None should always be done with is or is not, never the equality operators.
For the students, this comes up in the first week of the course with lines like "if x == None:" which work perfectly with == but should use is/is-not for PEP8 conformance.
My guess is that this rule is in PEP8 because, within a Python implementation, it is within the programmer's mental model that, say, False is a singleton. The mandatory-is rule is in PEP8 to reinforce that mental model by requiring the is operator. Plus it probably runs a tiny bit faster.
However, for "regular" Python code, not implementing Python, forcing the use of is instead of the simpler == is unneeded and unhelpful (and analogously forcing "is not" when != works correctly). What is the benefit of forcing the is operator there? I would say it spreads an awareness of the details of how certain values are allocated within Python. That's not much of a benefit, and it's kind of circular. Like if programmers were permitted to use ==, they wouldn't need to know the details of how Python allocates those values. Being shielded from implementation details is a Python strength - think of the Java vs. Python story above. Is Java better because it builds an awareness in the programmer of the different comparison functions for different types? Of course not! Python is better in that case because it lets the programmer simply use == and not think about those details. Understanding the singleton strategy is important in some corners of coding, but forcing the is operator on all Python code is way out of proportion to the benefit.
As a practical matter, the way this comes up for my students is that IDEs by default will put warning marks around PEP8 violations in their code. Mostly this IDE-coaching is very helpful for students learning Python. For example, It's great that beginning Python programmers learn to put one space around operators right from the first day. Having taught thousands of introductory Python students, the one PEP8 rule that causes problems is this mandatory-is rule.
As a teacher, this is especially jarring since the "just use ==" rule is so effortless to use correctly. In contrast, the mandatory-is rule adds a little pause where the programmer should think about which comparison operator is the correct one to use. It's not hard, but it feels unnecessary.
As a contrasting example, in the language C, programmers need to understand == vs. is right from the first day. You can't get anything done in C without understanding that distinction. However that is just not true for regular (not-Python-implementation) Python code, where == works correctly for the great majority of cases.
Here is my proposal:
Add the following parenthetical to the mandatory-is rule: (this rule is optional for code that is not part of an implementation of Python).
So in effect, programmers outside of a Python implementation can choose to use == or is for the "if x == None:" case. In this way, PEP8 conforming code before the change is still conforming. Moving forward, I would expect that regular code will trend towards using == in such a case, reserving is for the rare cases where it is needed for correctness.
PEP8 was originally just for Python implementations, so why is this change needed? Because as a practical matter, the vast majority of code that is using PEP8 is not part of a Python implementation. This may not have been the original mission of PEP8, but it is how things have worked out.
Now we are in a situation where the rules in PEP8 are sent out to this ocean of Python programmers of many different ability levels writing regular code that is not a Python implementation. One could imagine a separate PEP800 style guide for regular code, but we don't need to do that, because in almost all cases PEP8 works great for regular code. I have taught thousands of new Python programmers, and the only place where PEP8 serves them poorly is this mandatory-is rule. Therefore instead of a separate style guide for regular code, I propose an exception for this one problem rule.
Ultimately this comes down to the question - should PEP8 push regular, not-Python-implementation code to use is for singletons in cases where == works perfectly? Seeing how effortless it is for programmers to use == as their first choice, I think PEP8 should allow that practice.
Best,
Nick
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/UZOUHF... Code of Conduct: http://python.org/psf/codeofconduct/
EVERY line you found would behave incorrectly if changed to "x == None".
Hi David, well it seems possible, but I don't see it, so maybe you can give an example. So here's the first line from the list: if file is not None: I think we should allow the programmer to use != for that line. Can you describe a scenario where that line written with != gets the wrong answer? I can sort of think of how to get it to work wrong, but only with the most unbelievable contortions. I'm not saying that uses for "is" don't exist - if someone is coding up something where detecting if something is a copy is needed, then "is" is the way. However, requiring "is" for None and False and all these other super-common singletons .. I don't see why we can't just let them use ==. Best, Nick On Mon, Aug 30, 2021 at 2:04 PM David Mertz, Ph.D. <david.mertz@gmail.com> wrote:
EVERY line you found would behave incorrectly if changed to "x == None".
Admittedly, doing so would require setting the various variables and attributes to somewhat uncommon objects that act "funny" when comparing to None. Or when doing equality comparisons in general.
As others have said, learning the difference between equality and Identity is relevant pretty early in learning to program. No, not in the first day. Probably not on the second. But PEP 8 shouldn't be distorted just for people in their first month of programming... who are unlikely to be contributors to CPython so soon, as well.
As soon as you teach the difference between:
a, b = [], []
and:
a = b = []
That distinction has become very important. Knowing operators to express that distinction is thereby important.
On Mon, Aug 30, 2021, 4:51 PM Nick Parlante <nick@cs.stanford.edu> wrote:
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()
On Mon, Aug 30, 2021 at 11:32 AM Nick Parlante <nick@cs.stanford.edu> wrote:
Hi there python-ideas - I've been teaching Python as a first programming language for a few years, and from that experience I want to propose a change to PEP8. I'm sure the default position for PEP8 is to avoid changing it. However, for this one rule I think a good case can be made to make it optional, so let me know what you think.
Let me start with what I've learned from teaching students in Java and now in Python. In Java, you use == for ints, but you need to use equals() for strings. Of course students screw this up constantly, using == in a context that calls for equals() and their code does not work right. Then for Java arrays a different comparison function is required, and so it goes. To teach comparisons in Python, I simply say "just use ==" - it works for ints, for strings, even for lists. Students are blown away by how nice and simple this is. This is how things should work. Python really gets this right.
So what is the problem?
The problem for Python is what I will call the "mandatory-is" rule in PEP8, which reads:
Comparisons to singletons like None should always be done with is or is not, never the equality operators.
For the students, this comes up in the first week of the course with lines like "if x == None:" which work perfectly with == but should use is/is-not for PEP8 conformance.
My guess is that this rule is in PEP8 because, within a Python implementation, it is within the programmer's mental model that, say, False is a singleton. The mandatory-is rule is in PEP8 to reinforce that mental model by requiring the is operator. Plus it probably runs a tiny bit faster.
However, for "regular" Python code, not implementing Python, forcing the use of is instead of the simpler == is unneeded and unhelpful (and analogously forcing "is not" when != works correctly). What is the benefit of forcing the is operator there? I would say it spreads an awareness of the details of how certain values are allocated within Python. That's not much of a benefit, and it's kind of circular. Like if programmers were permitted to use ==, they wouldn't need to know the details of how Python allocates those values. Being shielded from implementation details is a Python strength - think of the Java vs. Python story above. Is Java better because it builds an awareness in the programmer of the different comparison functions for different types? Of course not! Python is better in that case because it lets the programmer simply use == and not think about those details. Understanding the singleton strategy is important in some corners of coding, but forcing the is operator on all Python code is way out of proportion to the benefit.
As a practical matter, the way this comes up for my students is that IDEs by default will put warning marks around PEP8 violations in their code. Mostly this IDE-coaching is very helpful for students learning Python. For example, It's great that beginning Python programmers learn to put one space around operators right from the first day. Having taught thousands of introductory Python students, the one PEP8 rule that causes problems is this mandatory-is rule.
As a teacher, this is especially jarring since the "just use ==" rule is so effortless to use correctly. In contrast, the mandatory-is rule adds a little pause where the programmer should think about which comparison operator is the correct one to use. It's not hard, but it feels unnecessary.
As a contrasting example, in the language C, programmers need to understand == vs. is right from the first day. You can't get anything done in C without understanding that distinction. However that is just not true for regular (not-Python-implementation) Python code, where == works correctly for the great majority of cases.
Here is my proposal:
Add the following parenthetical to the mandatory-is rule: (this rule is optional for code that is not part of an implementation of Python).
So in effect, programmers outside of a Python implementation can choose to use == or is for the "if x == None:" case. In this way, PEP8 conforming code before the change is still conforming. Moving forward, I would expect that regular code will trend towards using == in such a case, reserving is for the rare cases where it is needed for correctness.
PEP8 was originally just for Python implementations, so why is this change needed? Because as a practical matter, the vast majority of code that is using PEP8 is not part of a Python implementation. This may not have been the original mission of PEP8, but it is how things have worked out.
Now we are in a situation where the rules in PEP8 are sent out to this ocean of Python programmers of many different ability levels writing regular code that is not a Python implementation. One could imagine a separate PEP800 style guide for regular code, but we don't need to do that, because in almost all cases PEP8 works great for regular code. I have taught thousands of new Python programmers, and the only place where PEP8 serves them poorly is this mandatory-is rule. Therefore instead of a separate style guide for regular code, I propose an exception for this one problem rule.
Ultimately this comes down to the question - should PEP8 push regular, not-Python-implementation code to use is for singletons in cases where == works perfectly? Seeing how effortless it is for programmers to use == as their first choice, I think PEP8 should allow that practice.
Best,
Nick
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/UZOUHF... Code of Conduct: http://python.org/psf/codeofconduct/
Chris Angelica and Matthias Bussonnier have provided numerous examples of both "custom" objects and widely used libraries where the distinction between equality and Identity is important. Nearly every example you found is such a case. There is an attribute or a variable (often a formal parameter) that uses None as a sentinel for "not set" or "missing". They do that because I'm other circumstances they WILL be set. It is true that if 'myvar' happens to be set 37 or "hello", then 'myvar == None' or 'myvar is None' are equivalent. But that only works for a limited range of possible values. As I noted, the same day you teach students about mutable collections, you need to teach them the difference between Identity and equality. If you fall to do so, you do them a disservice. On Mon, Aug 30, 2021, 5:31 PM Nick Parlante <nick@cs.stanford.edu> wrote:
EVERY line you found would behave incorrectly if changed to "x == None".
Hi David, well it seems possible, but I don't see it, so maybe you can give an example.
So here's the first line from the list:
if file is not None:
I think we should allow the programmer to use != for that line. Can you describe a scenario where that line written with != gets the wrong answer? I can sort of think of how to get it to work wrong, but only with the most unbelievable contortions.
I'm not saying that uses for "is" don't exist - if someone is coding up something where detecting if something is a copy is needed, then "is" is the way. However, requiring "is" for None and False and all these other super-common singletons .. I don't see why we can't just let them use ==.
Best,
Nick
On Mon, Aug 30, 2021 at 2:04 PM David Mertz, Ph.D. <david.mertz@gmail.com> wrote:
EVERY line you found would behave incorrectly if changed to "x == None".
Admittedly, doing so would require setting the various variables and attributes to somewhat uncommon objects that act "funny" when comparing to None. Or when doing equality comparisons in general.
As others have said, learning the difference between equality and Identity is relevant pretty early in learning to program. No, not in the first day. Probably not on the second. But PEP 8 shouldn't be distorted just for people in their first month of programming... who are unlikely to be contributors to CPython so soon, as well.
As soon as you teach the difference between:
a, b = [], []
and:
a = b = []
That distinction has become very important. Knowing operators to express that distinction is thereby important.
On Mon, Aug 30, 2021, 4:51 PM Nick Parlante <nick@cs.stanford.edu> wrote:
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()
On Mon, Aug 30, 2021 at 11:32 AM Nick Parlante <nick@cs.stanford.edu> wrote:
Hi there python-ideas - I've been teaching Python as a first programming language for a few years, and from that experience I want to propose a change to PEP8. I'm sure the default position for PEP8 is to avoid changing it. However, for this one rule I think a good case can be made to make it optional, so let me know what you think.
Let me start with what I've learned from teaching students in Java and now in Python. In Java, you use == for ints, but you need to use equals() for strings. Of course students screw this up constantly, using == in a context that calls for equals() and their code does not work right. Then for Java arrays a different comparison function is required, and so it goes. To teach comparisons in Python, I simply say "just use ==" - it works for ints, for strings, even for lists. Students are blown away by how nice and simple this is. This is how things should work. Python really gets this right.
So what is the problem?
The problem for Python is what I will call the "mandatory-is" rule in PEP8, which reads:
Comparisons to singletons like None should always be done with is or is not, never the equality operators.
For the students, this comes up in the first week of the course with lines like "if x == None:" which work perfectly with == but should use is/is-not for PEP8 conformance.
My guess is that this rule is in PEP8 because, within a Python implementation, it is within the programmer's mental model that, say, False is a singleton. The mandatory-is rule is in PEP8 to reinforce that mental model by requiring the is operator. Plus it probably runs a tiny bit faster.
However, for "regular" Python code, not implementing Python, forcing the use of is instead of the simpler == is unneeded and unhelpful (and analogously forcing "is not" when != works correctly). What is the benefit of forcing the is operator there? I would say it spreads an awareness of the details of how certain values are allocated within Python. That's not much of a benefit, and it's kind of circular. Like if programmers were permitted to use ==, they wouldn't need to know the details of how Python allocates those values. Being shielded from implementation details is a Python strength - think of the Java vs. Python story above. Is Java better because it builds an awareness in the programmer of the different comparison functions for different types? Of course not! Python is better in that case because it lets the programmer simply use == and not think about those details. Understanding the singleton strategy is important in some corners of coding, but forcing the is operator on all Python code is way out of proportion to the benefit.
As a practical matter, the way this comes up for my students is that IDEs by default will put warning marks around PEP8 violations in their code. Mostly this IDE-coaching is very helpful for students learning Python. For example, It's great that beginning Python programmers learn to put one space around operators right from the first day. Having taught thousands of introductory Python students, the one PEP8 rule that causes problems is this mandatory-is rule.
As a teacher, this is especially jarring since the "just use ==" rule is so effortless to use correctly. In contrast, the mandatory-is rule adds a little pause where the programmer should think about which comparison operator is the correct one to use. It's not hard, but it feels unnecessary.
As a contrasting example, in the language C, programmers need to understand == vs. is right from the first day. You can't get anything done in C without understanding that distinction. However that is just not true for regular (not-Python-implementation) Python code, where == works correctly for the great majority of cases.
Here is my proposal:
Add the following parenthetical to the mandatory-is rule: (this rule is optional for code that is not part of an implementation of Python).
So in effect, programmers outside of a Python implementation can choose to use == or is for the "if x == None:" case. In this way, PEP8 conforming code before the change is still conforming. Moving forward, I would expect that regular code will trend towards using == in such a case, reserving is for the rare cases where it is needed for correctness.
PEP8 was originally just for Python implementations, so why is this change needed? Because as a practical matter, the vast majority of code that is using PEP8 is not part of a Python implementation. This may not have been the original mission of PEP8, but it is how things have worked out.
Now we are in a situation where the rules in PEP8 are sent out to this ocean of Python programmers of many different ability levels writing regular code that is not a Python implementation. One could imagine a separate PEP800 style guide for regular code, but we don't need to do that, because in almost all cases PEP8 works great for regular code. I have taught thousands of new Python programmers, and the only place where PEP8 serves them poorly is this mandatory-is rule. Therefore instead of a separate style guide for regular code, I propose an exception for this one problem rule.
Ultimately this comes down to the question - should PEP8 push regular, not-Python-implementation code to use is for singletons in cases where == works perfectly? Seeing how effortless it is for programmers to use == as their first choice, I think PEP8 should allow that practice.
Best,
Nick
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/UZOUHF... Code of Conduct: http://python.org/psf/codeofconduct/
I agree that, of course, it's possible to construct a class where == has this kind of weird behavior, like claiming that == to None is True. So are you saying that the reason PEP8 should forbid writing the line like this if x == None: is because x might be such a class? Best, Nick On Mon, Aug 30, 2021 at 2:41 PM David Mertz, Ph.D. <david.mertz@gmail.com> wrote:
Chris Angelica and Matthias Bussonnier have provided numerous examples of both "custom" objects and widely used libraries where the distinction between equality and Identity is important.
Nearly every example you found is such a case. There is an attribute or a variable (often a formal parameter) that uses None as a sentinel for "not set" or "missing". They do that because I'm other circumstances they WILL be set.
It is true that if 'myvar' happens to be set 37 or "hello", then 'myvar == None' or 'myvar is None' are equivalent. But that only works for a limited range of possible values.
As I noted, the same day you teach students about mutable collections, you need to teach them the difference between Identity and equality. If you fall to do so, you do them a disservice.
On Mon, Aug 30, 2021, 5:31 PM Nick Parlante <nick@cs.stanford.edu> wrote:
EVERY line you found would behave incorrectly if changed to "x == None".
Hi David, well it seems possible, but I don't see it, so maybe you can give an example.
So here's the first line from the list:
if file is not None:
I think we should allow the programmer to use != for that line. Can you describe a scenario where that line written with != gets the wrong answer? I can sort of think of how to get it to work wrong, but only with the most unbelievable contortions.
I'm not saying that uses for "is" don't exist - if someone is coding up something where detecting if something is a copy is needed, then "is" is the way. However, requiring "is" for None and False and all these other super-common singletons .. I don't see why we can't just let them use ==.
Best,
Nick
On Mon, Aug 30, 2021 at 2:04 PM David Mertz, Ph.D. <david.mertz@gmail.com> wrote:
EVERY line you found would behave incorrectly if changed to "x == None".
Admittedly, doing so would require setting the various variables and attributes to somewhat uncommon objects that act "funny" when comparing to None. Or when doing equality comparisons in general.
As others have said, learning the difference between equality and Identity is relevant pretty early in learning to program. No, not in the first day. Probably not on the second. But PEP 8 shouldn't be distorted just for people in their first month of programming... who are unlikely to be contributors to CPython so soon, as well.
As soon as you teach the difference between:
a, b = [], []
and:
a = b = []
That distinction has become very important. Knowing operators to express that distinction is thereby important.
On Mon, Aug 30, 2021, 4:51 PM Nick Parlante <nick@cs.stanford.edu> wrote:
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()
On Mon, Aug 30, 2021 at 11:32 AM Nick Parlante <nick@cs.stanford.edu> wrote:
Hi there python-ideas - I've been teaching Python as a first programming language for a few years, and from that experience I want to propose a change to PEP8. I'm sure the default position for PEP8 is to avoid changing it. However, for this one rule I think a good case can be made to make it optional, so let me know what you think.
Let me start with what I've learned from teaching students in Java and now in Python. In Java, you use == for ints, but you need to use equals() for strings. Of course students screw this up constantly, using == in a context that calls for equals() and their code does not work right. Then for Java arrays a different comparison function is required, and so it goes. To teach comparisons in Python, I simply say "just use ==" - it works for ints, for strings, even for lists. Students are blown away by how nice and simple this is. This is how things should work. Python really gets this right.
So what is the problem?
The problem for Python is what I will call the "mandatory-is" rule in PEP8, which reads:
Comparisons to singletons like None should always be done with is or is not, never the equality operators.
For the students, this comes up in the first week of the course with lines like "if x == None:" which work perfectly with == but should use is/is-not for PEP8 conformance.
My guess is that this rule is in PEP8 because, within a Python implementation, it is within the programmer's mental model that, say, False is a singleton. The mandatory-is rule is in PEP8 to reinforce that mental model by requiring the is operator. Plus it probably runs a tiny bit faster.
However, for "regular" Python code, not implementing Python, forcing the use of is instead of the simpler == is unneeded and unhelpful (and analogously forcing "is not" when != works correctly). What is the benefit of forcing the is operator there? I would say it spreads an awareness of the details of how certain values are allocated within Python. That's not much of a benefit, and it's kind of circular. Like if programmers were permitted to use ==, they wouldn't need to know the details of how Python allocates those values. Being shielded from implementation details is a Python strength - think of the Java vs. Python story above. Is Java better because it builds an awareness in the programmer of the different comparison functions for different types? Of course not! Python is better in that case because it lets the programmer simply use == and not think about those details. Understanding the singleton strategy is important in some corners of coding, but forcing the is operator on all Python code is way out of proportion to the benefit.
As a practical matter, the way this comes up for my students is that IDEs by default will put warning marks around PEP8 violations in their code. Mostly this IDE-coaching is very helpful for students learning Python. For example, It's great that beginning Python programmers learn to put one space around operators right from the first day. Having taught thousands of introductory Python students, the one PEP8 rule that causes problems is this mandatory-is rule.
As a teacher, this is especially jarring since the "just use ==" rule is so effortless to use correctly. In contrast, the mandatory-is rule adds a little pause where the programmer should think about which comparison operator is the correct one to use. It's not hard, but it feels unnecessary.
As a contrasting example, in the language C, programmers need to understand == vs. is right from the first day. You can't get anything done in C without understanding that distinction. However that is just not true for regular (not-Python-implementation) Python code, where == works correctly for the great majority of cases.
Here is my proposal:
Add the following parenthetical to the mandatory-is rule: (this rule is optional for code that is not part of an implementation of Python).
So in effect, programmers outside of a Python implementation can choose to use == or is for the "if x == None:" case. In this way, PEP8 conforming code before the change is still conforming. Moving forward, I would expect that regular code will trend towards using == in such a case, reserving is for the rare cases where it is needed for correctness.
PEP8 was originally just for Python implementations, so why is this change needed? Because as a practical matter, the vast majority of code that is using PEP8 is not part of a Python implementation. This may not have been the original mission of PEP8, but it is how things have worked out.
Now we are in a situation where the rules in PEP8 are sent out to this ocean of Python programmers of many different ability levels writing regular code that is not a Python implementation. One could imagine a separate PEP800 style guide for regular code, but we don't need to do that, because in almost all cases PEP8 works great for regular code. I have taught thousands of new Python programmers, and the only place where PEP8 serves them poorly is this mandatory-is rule. Therefore instead of a separate style guide for regular code, I propose an exception for this one problem rule.
Ultimately this comes down to the question - should PEP8 push regular, not-Python-implementation code to use is for singletons in cases where == works perfectly? Seeing how effortless it is for programmers to use == as their first choice, I think PEP8 should allow that practice.
Best,
Nick
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/UZOUHF... Code of Conduct: http://python.org/psf/codeofconduct/
It really feels now like you are TRYING to miss the point of equality and identity, and why 'is None' checks are the correct approach to recommend. Here's a slightly contrived, but not absurd function. I've certainly written a great many that follow this general form.
def add_multiples(elements=None, multiples=None): ... if elements == None: ... # Default to the intergers 1 to 10 ... elements = list(range(1, 11)) ... if multiples == None: ... multiples = [1] * len(elements) ... total = 0 ... for e, m in zip(elements, multiples): ... total += e*m ... return total ... a = [13, 27, 52] b = [1, 2, 3] add_multiples(a, b) 223 add_multiples() 55
I was just reading something that mentioned the famous Gauss sum of sequential numbers :-). We can try this with other sequences of numbers where it should succeed (and DOES if I use the clear and obvious approach rather than deliberately confusing identity and equality):
c = np.array([13, 27, 52]) d = range(1, 1000) add_multiples(c, d) Traceback (most recent call last): File "<ipython-input-38-c4826ecd2a2e>", line 1, in <module> add_multiples(c, d) File "<ipython-input-8-8c58e2c77071>", line 2, in add_multiples if elements == None: ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
On Tue, Aug 31, 2021 at 8:54 AM Nick Parlante <nick@cs.stanford.edu> wrote:
I agree that, of course, it's possible to construct a class where == has this kind of weird behavior, like claiming that == to None is True.
So are you saying that the reason PEP8 should forbid writing the line like this
if x == None:
is because x might be such a class?
Yes, among other reasons. That's why identity checks are *the only correct way* to check for the identity of something. Equality is not a substitute. ChrisA
On Mon, Aug 30, 2021 at 01:50:47PM -0700, Nick Parlante wrote:
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)
You have a funny idea about what counts as a random selection. I hope you don't teach statistics as well as programming *wink* "To calculate average income, we take a random selection of people by choosing the first four people on the Forbes Rich List..." -- Steve
Nick Parlante wrote:
Hi there python-ideas - I've been teaching Python as a first programming language for a few years, and from that experience I want to propose a change to PEP8. I'm sure the default position for PEP8 is to avoid changing it. However, for this one rule I think a good case can be made to make it optional, so let me know what you think. Let me start with what I've learned from teaching students in Java and now in Python. In Java, you use == for ints, but you need to use equals() for strings. Of course students screw this up constantly, using == in a context that calls for equals() and their code does not work right. Then for Java arrays a different comparison function is required, and so it goes. To teach comparisons in Python, I simply say "just use ==" - it works for ints, for strings, even for lists. Students are blown away by how nice and simple this is. This is how things should work. Python really gets this right. So what is the problem? The problem for Python is what I will call the "mandatory-is" rule in PEP8, which reads: Comparisons to singletons like None should always be done with is or is not, never the equality operators. For the students, this comes up in the first week of the course with lines like "if x == None:" which work perfectly with == but should use is/is-not for PEP8 conformance. My guess is that this rule is in PEP8 because, within a Python implementation, it is within the programmer's mental model that, say, False is a singleton. The mandatory-is rule is in PEP8 to reinforce that mental model by requiring the is operator. Plus it probably runs a tiny bit faster. However, for "regular" Python code, not implementing Python, forcing the use of is instead of the simpler == is unneeded and unhelpful (and analogously forcing "is not" when != works correctly). What is the benefit of forcing the is operator there? I would say it spreads an awareness of the details of how certain values are allocated within Python. That's not much of a benefit, and it's kind of circular. Like if programmers were permitted to use ==, they wouldn't need to know the details of how Python allocates those values. Being shielded from implementation details is a Python strength - think of the Java vs. Python story above. Is Java better because it builds an awareness in the programmer of the different comparison functions for different types? Of course not! Python is better in that case because it lets the programmer simply use == and not think about those details. Understanding the singleton strategy is important in some corners of coding, but forcing the is operator on all Python code is way out of proportion to the benefit. As a practical matter, the way this comes up for my students is that IDEs by default will put warning marks around PEP8 violations in their code. Mostly this IDE-coaching is very helpful for students learning Python. For example, It's great that beginning Python programmers learn to put one space around operators right from the first day. Having taught thousands of introductory Python students, the one PEP8 rule that causes problems is this mandatory-is rule. As a teacher, this is especially jarring since the "just use ==" rule is so effortless to use correctly. In contrast, the mandatory-is rule adds a little pause where the programmer should think about which comparison operator is the correct one to use. It's not hard, but it feels unnecessary. As a contrasting example, in the language C, programmers need to understand == vs. is right from the first day. You can't get anything done in C without understanding that distinction. However that is just not true for regular (not-Python-implementation) Python code, where == works correctly for the great majority of cases. Here is my proposal: Add the following parenthetical to the mandatory-is rule: (this rule is optional for code that is not part of an implementation of Python). So in effect, programmers outside of a Python implementation can choose to use == or is for the "if x == None:" case. In this way, PEP8 conforming code before the change is still conforming. Moving forward, I would expect that regular code will trend towards using == in such a case, reserving is for the rare cases where it is needed for correctness. PEP8 was originally just for Python implementations, so why is this change needed? Because as a practical matter, the vast majority of code that is using PEP8 is not part of a Python implementation. This may not have been the original mission of PEP8, but it is how things have worked out. Now we are in a situation where the rules in PEP8 are sent out to this ocean of Python programmers of many different ability levels writing regular code that is not a Python implementation. One could imagine a separate PEP800 style guide for regular code, but we don't need to do that, because in almost all cases PEP8 works great for regular code. I have taught thousands of new Python programmers, and the only place where PEP8 serves them poorly is this mandatory-is rule. Therefore instead of a separate style guide for regular code, I propose an exception for this one problem rule. Ultimately this comes down to the question - should PEP8 push regular, not-Python-implementation code to use is for singletons in cases where == works perfectly? Seeing how effortless it is for programmers to use == as their first choice, I think PEP8 should allow that practice. Best, Nick
I've only really seen this in practice in sqlalchemy and it has espr.is_(None) and expr.is_not_(None) and ~expr for expr == None and expr != None and expr == True
On Mon, Aug 30, 2021 at 11:32:20AM -0700, Nick Parlante wrote:
Hi there python-ideas - I've been teaching Python as a first programming language for a few years, and from that experience I want to propose a change to PEP8. I'm sure the default position for PEP8 is to avoid changing it. However, for this one rule I think a good case can be made to make it optional, so let me know what you think.
I think you care too much about what PEP-8 says, and too little about why we use idiomatic language. PEP-8 is not mandatory outside of the stdlib, and it is not intended to be best practice for teaching beginners, so why do you care so much about PEP-8? Just teach whatever standard you want. If you don't want to introduce `is` in your beginners course, that's fine. But you should consider that good code is supposed to express the programmer's *intent*. The intent of using `obj is None` is to detect when the object `obj` is not just some value that happens to mimic None-ness by behaving the same, or some other object that compares equal to None, it actually *is* the None singleton. (And None is always a singleton. That's a language guarantee.) To put it in real world terms, I don't want a reproduction of the Mona Lisa, no matter how accurate it may be, I want the *actual* Mona Lisa. It really doesn't matter that your students will probably never come across any sort of object that compares equal to None apart from None itself. The *intent* is what matters: using `obj is None` tells the reader clearly and idiomatically that you want the None singleton itself, and no other value or object no matter how closely it mimics None. If by some unlikely chance or contrived circumstances your code has something which mimics None and compares equal to it, `if obj is None` says clearly that you *intend* to only accept the None singleton itself and no other value or object no matter how closely it mimics None. As I said, PEP 8 is not mandatory, so teach whatever you like. But `is` exists for a reason, and the idiom `obj is None` is not about slavishly following a style guide, or performance, or about the vanishingly small chance that you actually will come across something that compares equal to None. It is about sending a message to the reader that you want None and will accept no substitutes.
To teach comparisons in Python, I simply say "just use ==" - it works for ints, for strings, even for lists. Students are blown away by how nice and simple this is. This is how things should work. Python really gets this right.
Great. Go right ahead then. It's your course, you can teach whatever you like. But if it were *my* course, rather than yours, I would teach a rule that is only a tiny bit more complex than "always use `==`": Always use `==`, *except* when comparing to None, then always use `is`. That's close enough for beginners. But as I said, if you want to only teach `==`, go right ahead. -- Steve
On Mon, Aug 30, 2021 at 11:32:20AM -0700, Nick Parlante wrote:
As a practical matter, the way this comes up for my students is that IDEs by default will put warning marks around PEP8 violations in their code.
I'm not fond of linters that flag PEP 8 violations. And if I recall correctly, I think Guido is likewise not convinced that they are a good idea. Anyway, it doesn't matter. Your IDE ought to be configurable to enable or disable that rule. For example: https://www.jetbrains.com/help/pycharm/configuring-code-style.html You ought to be able to come up with a config that disables that specific rule, and distribute it to your students automatically. You may need to determine which linter the IDE uses to check for style violations, and provide a config specifically for that linter. If you tell us what IDE your school or department uses, we might even be able to tell you what config you need. And in practical terms, that will likely be much, much faster than waiting for us to change PEP 8, the linters to change their tests, and the IDEs to be updated to use the new versions of the linters.
Here is my proposal:
Add the following parenthetical to the mandatory-is rule: (this rule is optional for code that is not part of an implementation of Python).
None of PEP-8 is mandatory except for the stdlib, and even there, it's more of a guideline than a rule. Why do PEP-8 zealots never, ever read the most important rule of PEP-8? It's right at the top too, just after the introduction. (Sorry Nick, this is not aimed at you personally.) https://www.python.org/dev/peps/pep-0008/#a-foolish-consistency-is-the-hobgo... -- Steve
On Tue, Aug 31, 2021 at 11:33:16AM +1000, Steven D'Aprano wrote:
If you tell us what IDE your school or department uses, we might even be able to tell you what config you need.
Here are instructions for PyCharm: http://iambigblind.blogspot.com/2013/02/configuring-pep8py-support-in-pychar... The code for "is None" checks is E711. https://pep8.readthedocs.io/en/release-1.7.x/intro.html#error-codes -- Steve
It's all been said, but a couple notes: I also teach beginners Python -- and I absolutely teach them to use "is None" (I'd have to look at my notes, but I think around lesson 3), In fact, I also teach about what ``is`` means, and how it is very different from ``==``. I have seen all too many instances of code like: if c is 0: ... Which actually DOES work, for small integers, on cPython -- but it is a very bad idea. (though I think it raises a Warning now) And your students are almost guaranteed to encounter an occasion where using == None causes problems at some point in their programming careers -- much better to be aware of it early on! As others have said, there is no such thing as "mandatory" for PEP 8. It is a "style" guide. Though interestingly, the recommendation of using is with None is not just one of style, there is a real semantic difference. In fact, of all the things in PEP 8, this might actually be one of the more important! I encourage you NOT to turn it off in your students' linter. -CHB -- Christopher Barker, PhD (Chris) Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
Hi Steven, I wish I could blame this on zealots, so let me talk a little bit about how PEP8 works in education, and I'll talk about moving forward in another message. As mentioned, PEP8 is formally for narrow cases, and includes disclaimers about not applying it mechanically, reflecting a basic reasonableness. Nothing to complain about there. As a practical matter, the IDEs just default to having PEP8 checking on, and they do it in a very visual way - akin to a text editor which puts little squiggles under words it thinks are misspelled. This maybe sounds annoying, but looking at how people learn to code, it's actually fantastic, and a giant success for the goals of PEP8. Instead of spending lecture time at some point reviewing rules about spacing or whatever, the students are absorbing that stuff just imperceptibly as the IDE is nudging them the right way by putting little colored marks around code it doesn't like. PEP8 contains a bunch of conventions, and now you have this crop of junior programmers who have absorbed the important ones, thus avoiding future pointless bikeshed wars. I hope people who care about PEP8 can have a moment of satisfaction, appreciating how IDEs have become a sort of gamified instrument to bring PEP8 to the world at low cost. So that's the good news. The visual quality that makes the IDEs so effective, also loses all the nuance. I have tried to explain to the students this or that they can let slide, but as a matter of human nature, it's hopeless. They want to get rid of the IDE marked code. And keep in mind, their devotion to that is mostly a force for good. So this basically great, but rather inflexible dynamic of the students, the IDEs, and PEP8 is what brings me here. I want to get to a world that is, let's say, "== tolerant". I claim you can teach a nice intro course using == early in the course, and many people have chimed in that they feel there's value in "is". I want to work out a solution between PEP8 and the IDEs where both approaches are permitted. Let me write up some proposals in a separate message, and people can comment on those. Best, Nick On Mon, Aug 30, 2021 at 6:36 PM Steven D'Aprano <steve@pearwood.info> wrote:
On Mon, Aug 30, 2021 at 11:32:20AM -0700, Nick Parlante wrote:
As a practical matter, the way this comes up for my students is that IDEs by default will put warning marks around PEP8 violations in their code.
I'm not fond of linters that flag PEP 8 violations. And if I recall correctly, I think Guido is likewise not convinced that they are a good idea.
Anyway, it doesn't matter. Your IDE ought to be configurable to enable or disable that rule. For example:
https://www.jetbrains.com/help/pycharm/configuring-code-style.html
You ought to be able to come up with a config that disables that specific rule, and distribute it to your students automatically. You may need to determine which linter the IDE uses to check for style violations, and provide a config specifically for that linter.
If you tell us what IDE your school or department uses, we might even be able to tell you what config you need.
And in practical terms, that will likely be much, much faster than waiting for us to change PEP 8, the linters to change their tests, and the IDEs to be updated to use the new versions of the linters.
Here is my proposal:
Add the following parenthetical to the mandatory-is rule: (this rule is optional for code that is not part of an implementation of Python).
None of PEP-8 is mandatory except for the stdlib, and even there, it's more of a guideline than a rule.
Why do PEP-8 zealots never, ever read the most important rule of PEP-8? It's right at the top too, just after the introduction. (Sorry Nick, this is not aimed at you personally.)
https://www.python.org/dev/peps/pep-0008/#a-foolish-consistency-is-the-hobgo...
-- Steve _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/QSECKF... Code of Conduct: http://python.org/psf/codeofconduct/
On 2021-08-31 at 11:15:22 -0700, Nick Parlante <nick@cs.stanford.edu> wrote:
As mentioned, PEP8 is formally for narrow cases, and includes disclaimers about not applying it mechanically, reflecting a basic reasonableness. Nothing to complain about there.
As a practical matter, the IDEs just default to having PEP8 checking on, and they do it in a very visual way - akin to a text editor which puts little squiggles under words it thinks are misspelled.
Aha: The IDEs are the zealots. :-) (I might liken PEP8 violations to natural language grammar recommendations rather than spelling mistakes: words are either misspelled or they're not; many grammar "violations" are matters of style, individual preference, or accepted norms that change over time.) I'm not sure I have much to add that hasn't been said already, except perhaps that your issue is, indeed, with the IDEs and the linters rather than with PEP8. Do you tell your students which IDE(s) and linters to use, and/or how to set them up? [...]
I want to get to a world that is, let's say, "== tolerant" ...
Do you use floating point values in your course?
I want to get to a world that is, let's say, "== tolerant" ...
Do you use floating point values in your course?
You will be relieved to know that we have great fun showing them how == does not work for floating point numbers. It is super memorable how that all comes unglued. Best, Nick On Tue, Aug 31, 2021 at 12:19 PM <2QdxY4RzWzUUiLuE@potatochowder.com> wrote:
On 2021-08-31 at 11:15:22 -0700, Nick Parlante <nick@cs.stanford.edu> wrote:
As mentioned, PEP8 is formally for narrow cases, and includes disclaimers about not applying it mechanically, reflecting a basic reasonableness. Nothing to complain about there.
As a practical matter, the IDEs just default to having PEP8 checking on, and they do it in a very visual way - akin to a text editor which puts little squiggles under words it thinks are misspelled.
Aha: The IDEs are the zealots. :-)
(I might liken PEP8 violations to natural language grammar recommendations rather than spelling mistakes: words are either misspelled or they're not; many grammar "violations" are matters of style, individual preference, or accepted norms that change over time.)
I'm not sure I have much to add that hasn't been said already, except perhaps that your issue is, indeed, with the IDEs and the linters rather than with PEP8.
Do you tell your students which IDE(s) and linters to use, and/or how to set them up?
[...]
I want to get to a world that is, let's say, "== tolerant" ...
Do you use floating point values in your course? _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/WFXZV2... Code of Conduct: http://python.org/psf/codeofconduct/
On Wed, Sep 1, 2021 at 5:35 AM Nick Parlante <nick@cs.stanford.edu> wrote:
I want to get to a world that is, let's say, "== tolerant" ...
Do you use floating point values in your course?
You will be relieved to know that we have great fun showing them how == does not work for floating point numbers. It is super memorable how that all comes unglued.
*sigh* Falsehoods programmers believe about floating point: "Equality is broken". ChrisA
On Tue, Aug 31, 2021 at 12:35:05PM -0700, Nick Parlante wrote:
You will be relieved to know that we have great fun showing them how == does not work for floating point numbers. It is super memorable how that all comes unglued.
Of course `==` works for floating point numbers. For floats, `x == y` returns True if and only if: - x and y are both zeroes - x and y are both +INF - x and y are both -INF - or x and y have the same numeric value. In other words, it is the intuitive and correct meaning of "equals" for all numbers capable of being represented as a float. People who remember floating point maths on computers prior to the widespread adoption of IEEE-754 will know that this is actually quite a big deal, and not something to sneer at. What doesn't work, for some definition of "doesn't", is everything else about floats: addition, subtraction, multiplication, conversion from decimal, etc. But *equality* is one of the few operations that works exactly. -- Steve
On Wed, Sep 1, 2021 at 10:23 AM Steven D'Aprano <steve@pearwood.info> wrote:
What doesn't work, for some definition of "doesn't", is everything else about floats: addition, subtraction, multiplication, conversion from decimal, etc. But *equality* is one of the few operations that works exactly.
Really, the only thing that BADLY doesn't work is conversion from decimal. All the others are accurate to within the available precision. The problem with binary floating point isn't equality; it's that humans assume that 0.1 is a precise value. We don't expect 0.33333 to be a precise representation of one third, yet we expect a computer to be able to store one tenth with perfect accuracy. That's where ALL the problems come from. In the classic demonstration that 0.1 + 0.2 != 0.3, the problem isn't equality, it's that none of those values is what you think it is. If I told you that 33/100 + 66/100 != 100/100, nobody would bat an eyelid. ChrisA
Thanks to everyone for many thoughtful comments. I'm going to try to wrap this up. So I talked above about how the IDEs have this very visual way of nudging beginning students to write their code to conform to PEP8, and this is the IDE default. Mostly this is a great, constructive dynamic, nudging students to follow the community standard. As someone who spends all their time in the land of Python-beginners, I hope this little report of how the IDEs nudge the beginners can be taken as a success story by people who care about PEP8. I should say, I'm not worried about Stanford students, they're going to be fine. In reality, I'm playing a long game here. I'm pushing for Python to grow to be the dominant intro teaching language (currently it's Java). I do not want the tooling by default to be flagging correct uses of == for, say, a highschool student a few years from now who is firing up the IDE to get started coding. Anyway, this is why I'm trying to get a little shift in the IDE default now. I want to get a phrasing into PEP8 so I can go to PyCharm et al, and get a little shift in their default behavior. PEP8 coloring in the IDE should remain on by default. That has many fantastic qualities as it works with the beginning students. But in the default configuration, I want the PEP8 scoring to be tolerant about "== None" for non-Python-implementation code. My original proposal was to add this parenthetical to the mandatory-is rule: "(this rule is optional for code that is not part of a Python implementation)". Or maybe just a weak nudge to the tooling, like "(it's permissible for tooling to leave E711 off by default, though it is required for Python implementation code)". (ht Steven D'Aprano pointing out the proper numbers https://pep8.readthedocs.io/en/release-1.7.x/intro.html#error-codes) Then I can go to PyCharm et al and say - hey, for new empty projects could you please default to PEP8 checking on but E711 off. Is there support for such a change? The replies on this thread I would say are mostly not supportive... Just using Todd recent reply as an example - describing == as "brittle". Then some point out that understanding equality and identity is important, so in effect this justifies getting "is" in front of the students even where == works. Then there's a practical view that being tolerant of "== None" may be a thing, but messing with PEP8 is not the way. Is there anyone other than me who would like to push for "== None tolerant" carve out for non-Python-implementation code? Best, Nick On Tue, Aug 31, 2021 at 12:19 PM <2QdxY4RzWzUUiLuE@potatochowder.com> wrote:
On 2021-08-31 at 11:15:22 -0700, Nick Parlante <nick@cs.stanford.edu> wrote:
As mentioned, PEP8 is formally for narrow cases, and includes disclaimers about not applying it mechanically, reflecting a basic reasonableness. Nothing to complain about there.
As a practical matter, the IDEs just default to having PEP8 checking on, and they do it in a very visual way - akin to a text editor which puts little squiggles under words it thinks are misspelled.
Aha: The IDEs are the zealots. :-)
(I might liken PEP8 violations to natural language grammar recommendations rather than spelling mistakes: words are either misspelled or they're not; many grammar "violations" are matters of style, individual preference, or accepted norms that change over time.)
I'm not sure I have much to add that hasn't been said already, except perhaps that your issue is, indeed, with the IDEs and the linters rather than with PEP8.
Do you tell your students which IDE(s) and linters to use, and/or how to set them up?
[...]
I want to get to a world that is, let's say, "== tolerant" ...
Do you use floating point values in your course? _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/WFXZV2... Code of Conduct: http://python.org/psf/codeofconduct/
On Wed, Sep 1, 2021 at 6:06 AM Nick Parlante <nick@cs.stanford.edu> wrote:
Is there anyone other than me who would like to push for "== None tolerant" carve out for non-Python-implementation code?
What you're asking is: Is there anyone other than you who would prefer for Python to officially encourage people to write buggy and unreliable code? No. I'm done discussing this; if you still genuinely think that "== None" is actually better than "is None", despite all the demonstrations posted, there's no convincing you. ChrisA
Hi Chris - well maybe we're looking at different questions. Your examples show it is possible to construct a data type where == None does not work. Clearly that is possible. 1. One conclusion is that the possibility of such == means that in general the == None form is unreliable. This is the proper mindset of the Python implementation, where the classes that need to work are practically unlimited, so relying on == would be unreliable as shown. 2. However, I am interested in the mass of ordinary looking programs that use strings, lists, ints, dicts, tuples, functions etc. to solve some problem. It's extremely likely that all of the classes in such a program have a reasonable definitions of ==. So if the programmer knows that, they can use the "== None" form in complete confidence. It works perfectly. It's possible to treat (1) as kind definitive, like that possibility ends the argument. I agree (1) is true, but do not find it convincing about the (2) case. I think (2) is more useful for thinking about Python programs in the world. Best, Nick On Tue, Aug 31, 2021 at 1:31 PM Chris Angelico <rosuav@gmail.com> wrote:
On Wed, Sep 1, 2021 at 6:06 AM Nick Parlante <nick@cs.stanford.edu> wrote:
Is there anyone other than me who would like to push for "== None tolerant" carve out for non-Python-implementation code?
What you're asking is: Is there anyone other than you who would prefer for Python to officially encourage people to write buggy and unreliable code?
No.
I'm done discussing this; if you still genuinely think that "== None" is actually better than "is None", despite all the demonstrations posted, there's no convincing you.
ChrisA _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/KE4C6L... Code of Conduct: http://python.org/psf/codeofconduct/
If it is not 100% obvious, I would strongly advice any IDE or linter against disabling E711 as a default. I'm not a contributors to any of those, so it's not my decision. But were anyone to ask me... Moreover, I would strongly discourage any instructor from papering over the difference between equality and Identity. I guess in a pure functional language there's no difference. But in Python it's of huge importance. As noted REPEATEDLY, this isn't just about 'is None'. As soon as you see these, it is a CRUCIAL distinction: a = b = [] c, d = [], [] Nary a None in sight, yet the distinction is key. On Tue, Aug 31, 2021, 5:23 PM Nick Parlante <nick@cs.stanford.edu> wrote:
Hi Chris - well maybe we're looking at different questions. Your examples show it is possible to construct a data type where == None does not work. Clearly that is possible.
1. One conclusion is that the possibility of such == means that in general the == None form is unreliable. This is the proper mindset of the Python implementation, where the classes that need to work are practically unlimited, so relying on == would be unreliable as shown.
2. However, I am interested in the mass of ordinary looking programs that use strings, lists, ints, dicts, tuples, functions etc. to solve some problem. It's extremely likely that all of the classes in such a program have a reasonable definitions of ==. So if the programmer knows that, they can use the "== None" form in complete confidence. It works perfectly.
It's possible to treat (1) as kind definitive, like that possibility ends the argument. I agree (1) is true, but do not find it convincing about the (2) case. I think (2) is more useful for thinking about Python programs in the world.
Best,
Nick
On Tue, Aug 31, 2021 at 1:31 PM Chris Angelico <rosuav@gmail.com> wrote:
On Wed, Sep 1, 2021 at 6:06 AM Nick Parlante <nick@cs.stanford.edu> wrote:
Is there anyone other than me who would like to push for "== None tolerant" carve out for non-Python-implementation code?
What you're asking is: Is there anyone other than you who would prefer for Python to officially encourage people to write buggy and unreliable code?
No.
I'm done discussing this; if you still genuinely think that "== None" is actually better than "is None", despite all the demonstrations posted, there's no convincing you.
ChrisA _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/KE4C6L... Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/KXWBSE... Code of Conduct: http://python.org/psf/codeofconduct/
On Tue, Aug 31, 2021, 17:24 Nick Parlante <nick@cs.stanford.edu> wrote:
Hi Chris - well maybe we're looking at different questions. Your examples show it is possible to construct a data type where == None does not work. Clearly that is possible.
1. One conclusion is that the possibility of such == means that in general the == None form is unreliable. This is the proper mindset of the Python implementation, where the classes that need to work are practically unlimited, so relying on == would be unreliable as shown.
2. However, I am interested in the mass of ordinary looking programs that use strings, lists, ints, dicts, tuples, functions etc. to solve some problem. It's extremely likely that all of the classes in such a program have a reasonable definitions of ==. So if the programmer knows that, they can use the "== None" form in complete confidence. It works perfectly.
It's possible to treat (1) as kind definitive, like that possibility ends the argument. I agree (1) is true, but do not find it convincing about the (2) case. I think (2) is more useful for thinking about Python programs in the world.
Best,
Nick
We have two approaches: 1. An approach that is guaranteed to always work no matter what. 2. An approach that will randomly break depending on potentially undocumented implementation details of the data you are using, including for some of th most common uses-cases for python. Both approaches use the same number of characters, the same complexity as far as users are concerned, and rely on concepts that anyone with a basic understanding of the language needs to know. So why would anyone choose to you approach 2? You insist that both approaches should be treated as equally valid. But they simply aren't. In the real world, where people are trying to do almost anything useful with python, approach 2 is too dangerous to rely on. There are just not that many real-world uses-cases that use nothing but the core python types. As you said, there are things that are subconsciously hard for people to avoid. One is leaving warnings in the IDE unfixed. Another, however, is forming habits. We want to encourage people to make good habits that will serve them well generally, not bad habits that wil usually work okay in a narrow context but will break everything as soon as they are applied in common, real-world situations. What you are advocating is a bad habit. Yes, it can work okay in some situations, but why should we be encouraging bad habits when there is no downside whatsoever to the good alternative? If you want to encourage bad habits you should be using perl or MATLAB.
Nick, On teh one hand, I teach a lot of beginners, and I share most of your experience with PEP8 and linters -- indeed, I encourage my studetns to use a linter right off the bat. (I don't require a particular editor, but I do provide recommendations for a handful of common ones (PyCharm, VS Code, Sublime), and I always recommend a linter. I also agree that the linter can be kind of distracting, and that some "problems" are more important than others, so it makes sense to turn off some particular issues. (not turned off, but I do extend the minimum line length myself) However, the "is None" recommendation is one I would NOT suggest my students turn off -- for all the reasons posted here. FRankly, I student that learns to write code with some not great spacing or naming conventions is much better shape than one that doesn't learn what `is` means in Python. I"ll say it again: recommending ``is None`` is not just "style" -- it has real different meaning. Also: it's not the job of the PEP 8 authors to rank the importance of the various recommendations. If you have an idea about a better linter configuration for beginners, then by all means create one, use it in your classes, publish it for other instructors to use, etc. -CHB NOTE: you must have different students than I do -- I have more problem with students ignoring a massive amount of cruft the linter is putting on their code ("I just want to get it to work!") than I do students obsessing over getting it lint free. NOTE 2: David Mertz had a very good point -- you can get very far in Python without using None at all (or at least not checking it - after all, functions do return None by default). Probably the most common use is to mean "not specified" in keyword parameters -- how early do you introduce those? -CHB -- Christopher Barker, PhD (Chris) Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
On Tue, Aug 31, 2021 at 06:18:15PM -0400, Todd wrote:
You insist that both approaches should be treated as equally valid. But they simply aren't. In the real world, where people are trying to do almost anything useful with python, approach 2 is too dangerous to rely on.
In fairness to Nick, he is not talking about the real world. Nick is talking about the hot-house environment of *education*, where fragile newbies are generally protected from real world issues. And let's not exaggerate the issue either by using emotive words like "dangerous". Outside of numpy arrays and pandas dataframes, I think it is highly unlikely that anyone in the real world will come across cases where `x == None` will cause your program to silently do the wrong thing in a way that can't be solved by telling them "well don't do that then". In almost all cases, the test we are looking at is used to decide whether or not to replace a parameter with a mutable default. So in practical terms, the worst thing that will happen is that the caller gets the default when they didn't want it. Numpy arrays and pandas dataframes are two big, important and common exceptions, but they are exceptional for a different reason: they don't even return True or False. Outside of contrived counter-examples, we're not likely to come across anything trying to mimac None in the real world. But that's not really why we use `is`, or at least, it's not the only reason. There are a bunch of reasons, none of which on their own are definitive, but together settle the issue (in my opinion). 1. Avoid rare bugs caused by weird objects. 2. Slightly faster and more efficient. 3. Expresses the programmer's intent. 4. Common idiom, so the reader doesn't have to think about it. -- Steve
On Wed, Sep 1, 2021 at 5:19 PM Steven D'Aprano <steve@pearwood.info> wrote:
Outside of contrived counter-examples, we're not likely to come across anything trying to mimac None in the real world. But that's not really why we use `is`, or at least, it's not the only reason. There are a bunch of reasons, none of which on their own are definitive, but together settle the issue (in my opinion).
1. Avoid rare bugs caused by weird objects. 2. Slightly faster and more efficient. 3. Expresses the programmer's intent. 4. Common idiom, so the reader doesn't have to think about it.
And in terms of education, these sorts of reasons are the things I tend to wrap up behind the description "best practice". For instance, why do we write file management using the 'with' statement? You could argue that it's because other Python implementations may behave differently, or future versions may behave differently, or there's the risk that the file wouldn't be closed because of some other reference... but the easiest explanation is "that's the standard idiom", and then you can explain the reasons as the justifications for it being standard. ChrisA
On 01.09.2021 10:27, Chris Angelico wrote:
On Wed, Sep 1, 2021 at 5:19 PM Steven D'Aprano <steve@pearwood.info> wrote:
Outside of contrived counter-examples, we're not likely to come across anything trying to mimac None in the real world. But that's not really why we use `is`, or at least, it's not the only reason. There are a bunch of reasons, none of which on their own are definitive, but together settle the issue (in my opinion).
1. Avoid rare bugs caused by weird objects. 2. Slightly faster and more efficient. 3. Expresses the programmer's intent. 4. Common idiom, so the reader doesn't have to think about it.
And in terms of education, these sorts of reasons are the things I tend to wrap up behind the description "best practice". For instance, why do we write file management using the 'with' statement? You could argue that it's because other Python implementations may behave differently, or future versions may behave differently, or there's the risk that the file wouldn't be closed because of some other reference... but the easiest explanation is "that's the standard idiom", and then you can explain the reasons as the justifications for it being standard.
I'm a bit puzzled by all this discussion around "is None". None is a singleton, so there can only be one such object in any Python process. Because there is only one, asking: if x is None: ... feels intuitive to me. if x == None: ... would also work, but misses the point about None being a singleton. BTW: In SQL you have to use "field IS NULL", "field = NULL" returns NULL, so you're not any smarter than before :-) -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Experts (#1, Sep 01 2021)
Python Projects, Coaching and Support ... https://www.egenix.com/ Python Product Development ... https://consulting.egenix.com/
::: We implement business ideas - efficiently in both time and costs ::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 https://www.egenix.com/company/contact/ https://www.malemburg.com/
On Wed, Sep 1, 2021 at 6:55 PM Marc-Andre Lemburg <mal@egenix.com> wrote:
BTW: In SQL you have to use "field IS NULL", "field = NULL" returns NULL, so you're not any smarter than before :-)
That's because NULL is kinda like None, kinda like NaN, kinda like "we don't have any data here", and kinda like "we don't know what we should store here, so store NULL". It is, I think, the weirdest data value ever to be processed by any human or computer. Except that it's not a value at all. Other than when it is. ChrisA
On Wed, Sep 1, 2021, 03:18 Steven D'Aprano <steve@pearwood.info> wrote:
On Tue, Aug 31, 2021 at 06:18:15PM -0400, Todd wrote:
You insist that both approaches should be treated as equally valid. But they simply aren't. In the real world, where people are trying to do almost anything useful with python, approach 2 is too dangerous to rely on.
In fairness to Nick, he is not talking about the real world. Nick is talking about the hot-house environment of *education*, where fragile newbies are generally protected from real world issues.
First, I address this later in my post. Using "== None" is a bad habit, there is simply no good reason to do it and lots of reasons not to, so students just shouldn't get in the habit of doing it in the first place. Second, in the post I was responding to Nick was explicitly suggesting "== None” be treated as an equally-acceptable approach in general. And let's not exaggerate the issue either by using emotive words like
"dangerous". Outside of numpy arrays and pandas dataframes, I think it is highly unlikely that anyone in the real world will come across cases where `x == None` will cause your program to silently do the wrong thing in a way that can't be solved by telling them "well don't do that then".
In almost all cases, the test we are looking at is used to decide whether or not to replace a parameter with a mutable default. So in practical terms, the worst thing that will happen is that the caller gets the default when they didn't want it.
Others in this thread have looked into this and there are a wide variety of other types that will suffer. More generally anything that raises an exception when given an invalid equality test, an extremely common idiom, will be fundamentally broken by this. It will be impossible to use them as arguments at all where "== None" is used, even in cases where they would otherwise work perfectly fine. Trying to do so will raise an exception.
In fairness to Nick, he is not talking about the real world. Nick is talking about the hot-house environment of *education*, where fragile newbies are generally protected from real world issues.
Let me unpack this just a teeny bit. We don't need to think of the students as fragile. Think of it as where the minutes go. Like why is Java worse? You are trying to explain about comparisons, and you need like 30 minutes for Java where the number of different required comparison facilities is big, and it just looks needlessly complicated. What is the problem with that? Think of it this way: I wanted to talk about *algorithms* and solving real problems .. like those are the best uses of lecture time and examples. If the language injects something that you feel is not such a great use of time, you notice that those minutes are taken away from the actual course goals. Now for Python, == vs. is nothing like that bad. Both == and is are sensible, necessary parts of the language, I would just prefer to talk about == earlier and is later. Of the two, == is the no-brainer one when the students just have ints and strings, and no-brainer is what you want in week 2. For teaching, you are not just identifying the 500 important things they need to know. To teach artfully, you are trying to think of an ordering, layering on 50 new things each week, where the subest achieved each week is internally coherent and you can do a project just using that subset, and at the end of the course you've gotten through everything. Best, Nick On Wed, Sep 1, 2021 at 12:17 AM Steven D'Aprano <steve@pearwood.info> wrote:
On Tue, Aug 31, 2021 at 06:18:15PM -0400, Todd wrote:
You insist that both approaches should be treated as equally valid. But they simply aren't. In the real world, where people are trying to do almost anything useful with python, approach 2 is too dangerous to rely on.
In fairness to Nick, he is not talking about the real world. Nick is talking about the hot-house environment of *education*, where fragile newbies are generally protected from real world issues.
And let's not exaggerate the issue either by using emotive words like "dangerous". Outside of numpy arrays and pandas dataframes, I think it is highly unlikely that anyone in the real world will come across cases where `x == None` will cause your program to silently do the wrong thing in a way that can't be solved by telling them "well don't do that then".
In almost all cases, the test we are looking at is used to decide whether or not to replace a parameter with a mutable default. So in practical terms, the worst thing that will happen is that the caller gets the default when they didn't want it.
Numpy arrays and pandas dataframes are two big, important and common exceptions, but they are exceptional for a different reason: they don't even return True or False.
Outside of contrived counter-examples, we're not likely to come across anything trying to mimac None in the real world. But that's not really why we use `is`, or at least, it's not the only reason. There are a bunch of reasons, none of which on their own are definitive, but together settle the issue (in my opinion).
1. Avoid rare bugs caused by weird objects. 2. Slightly faster and more efficient. 3. Expresses the programmer's intent. 4. Common idiom, so the reader doesn't have to think about it.
-- Steve _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/QQMLKJ... Code of Conduct: http://python.org/psf/codeofconduct/
On Wed, Sep 1, 2021 at 1:32 PM Nick Parlante <nick@cs.stanford.edu> wrote:
In fairness to Nick, he is not talking about the real world. Nick is
talking about the hot-house environment of *education*, where fragile newbies are generally protected from real world issues.
Let me unpack this just a teeny bit. We don't need to think of the students as fragile. Think of it as where the minutes go.
Like why is Java worse? You are trying to explain about comparisons, and you need like 30 minutes for Java where the number of different required comparison facilities is big, and it just looks needlessly complicated. What is the problem with that? Think of it this way: I wanted to talk about *algorithms* and solving real problems .. like those are the best uses of lecture time and examples. If the language injects something that you feel is not such a great use of time, you notice that those minutes are taken away from the actual course goals.
Now for Python, == vs. is nothing like that bad. Both == and is are sensible, necessary parts of the language, I would just prefer to talk about == earlier and is later. Of the two, == is the no-brainer one when the students just have ints and strings, and no-brainer is what you want in week 2.
For teaching, you are not just identifying the 500 important things they need to know. To teach artfully, you are trying to think of an ordering, layering on 50 new things each week, where the subest achieved each week is internally coherent and you can do a project just using that subset, and at the end of the course you've gotten through everything.
Best,
Nick
A couple people have said this but I'll ask again because I am curious: would it be possible to delay introduction of None entirely until it's time to introduce is? The biggest thing None seems to be needed for beginners is to learn the idiom for function signature default arguments for mutable parameters (the classic "Least Astonishment" and the Mutable Default Argument problem <https://stackoverflow.com/questions/1132941/least-astonishment-and-the-mutable-default-argument> ). If the goal of the course is to get as quickly as possible to *algorithms* and solving real problems, then I don't see why you even need to teach default arguments at all. --- Ricky. "I've never met a Kentucky man who wasn't either thinking about going home or actually going home." - Happy Chandler
Hi Ricky, A couple people have said this but I'll ask again because I am curious:
would it be possible to delay introduction of None entirely until it's time to introduce is?
This maybe comes down to course tactics. I happen to like doing algorithms with data arranged in 2d from the start - you can show the kind of before/after data state in a way that is easy for the students to see, to understand what their code needs to do, and the 2d is a rich enough domain that there's a zillion algorithms you can cook up. However, in the 2d, it comes up that you need a representation for when a square is empty, and I think None is the right value for that. Students have no problems understanding the role of None. You could use a string constant 'empty' or something as a workaround. But I think just showing the None is the right way. It's a Python course, here's a situation where None is the appropriate Python approach, so probably this is the week to show them None. A teacher could just use strings and ints and this would not come up, but I love the 2d early for the richness of the examples and homeworks you can do with it. Best, Nick On Wed, Sep 1, 2021 at 11:06 AM Ricky Teachey <ricky@teachey.org> wrote:
On Wed, Sep 1, 2021 at 1:32 PM Nick Parlante <nick@cs.stanford.edu> wrote:
In fairness to Nick, he is not talking about the real world. Nick is
talking about the hot-house environment of *education*, where fragile newbies are generally protected from real world issues.
Let me unpack this just a teeny bit. We don't need to think of the students as fragile. Think of it as where the minutes go.
Like why is Java worse? You are trying to explain about comparisons, and you need like 30 minutes for Java where the number of different required comparison facilities is big, and it just looks needlessly complicated. What is the problem with that? Think of it this way: I wanted to talk about *algorithms* and solving real problems .. like those are the best uses of lecture time and examples. If the language injects something that you feel is not such a great use of time, you notice that those minutes are taken away from the actual course goals.
Now for Python, == vs. is nothing like that bad. Both == and is are sensible, necessary parts of the language, I would just prefer to talk about == earlier and is later. Of the two, == is the no-brainer one when the students just have ints and strings, and no-brainer is what you want in week 2.
For teaching, you are not just identifying the 500 important things they need to know. To teach artfully, you are trying to think of an ordering, layering on 50 new things each week, where the subest achieved each week is internally coherent and you can do a project just using that subset, and at the end of the course you've gotten through everything.
Best,
Nick
A couple people have said this but I'll ask again because I am curious: would it be possible to delay introduction of None entirely until it's time to introduce is?
The biggest thing None seems to be needed for beginners is to learn the idiom for function signature default arguments for mutable parameters (the classic "Least Astonishment" and the Mutable Default Argument problem <https://stackoverflow.com/questions/1132941/least-astonishment-and-the-mutable-default-argument> ).
If the goal of the course is to get as quickly as possible to *algorithms* and solving real problems, then I don't see why you even need to teach default arguments at all.
--- Ricky.
"I've never met a Kentucky man who wasn't either thinking about going home or actually going home." - Happy Chandler
Sorry, replying to myself here, to complete the story here. Or put another way, None is a pretty core Python topic, so there's no sense in dancing around it too much. Really "is" is the problem. I'd rather get into the details of "is" later in the course. So I think two approaches are plausible here without going against this-thread too badly: 1. You could use "is", and say "It's most proper to use "is" when comparing to certain values, including None which we will explain later. For now,you just need to know that "is" plays a role similar to == here - testing if the thing is None." That would be fine. "I'm going to explain that later" is not that rare a thing to say in this course. 2. You could just use == like you do with strings and ints, and mention "it's more proper to use "is" with None here, and we'll start doing that when we get to "is" organically." This is the easier path for students - they are already using == for everything else and so it is zero extra thought for them to just use it with None. To be clear, their code will work perfectly with ==. They are just using strings and ints and whatever. When it's time for students to switch to using "is None" - the occasionally maligned IDE PEP8 coloring will be there to nudge them along! I've seen that nudge in action a bunch of times. Both of these strategies have some problems, so maybe we should just say that a particular teacher could follow their preference. Both strategies can get to the right place. BTW,, I think "is" works best about what you get to lists - so then you have copies and identity and equality are meaningful, but there's a lot of more basic stuff to get through before you get to lists. Like you don't want to do lists before you have loops and logic. Best, Nick On Wed, Sep 1, 2021 at 11:25 AM Nick Parlante <nick@cs.stanford.edu> wrote:
Hi Ricky,
A couple people have said this but I'll ask again because I am curious:
would it be possible to delay introduction of None entirely until it's time to introduce is?
This maybe comes down to course tactics. I happen to like doing algorithms with data arranged in 2d from the start - you can show the kind of before/after data state in a way that is easy for the students to see, to understand what their code needs to do, and the 2d is a rich enough domain that there's a zillion algorithms you can cook up. However, in the 2d, it comes up that you need a representation for when a square is empty, and I think None is the right value for that. Students have no problems understanding the role of None.
You could use a string constant 'empty' or something as a workaround. But I think just showing the None is the right way. It's a Python course, here's a situation where None is the appropriate Python approach, so probably this is the week to show them None.
A teacher could just use strings and ints and this would not come up, but I love the 2d early for the richness of the examples and homeworks you can do with it.
Best,
Nick
On Wed, Sep 1, 2021 at 11:06 AM Ricky Teachey <ricky@teachey.org> wrote:
On Wed, Sep 1, 2021 at 1:32 PM Nick Parlante <nick@cs.stanford.edu> wrote:
In fairness to Nick, he is not talking about the real world. Nick is
talking about the hot-house environment of *education*, where fragile newbies are generally protected from real world issues.
Let me unpack this just a teeny bit. We don't need to think of the students as fragile. Think of it as where the minutes go.
Like why is Java worse? You are trying to explain about comparisons, and you need like 30 minutes for Java where the number of different required comparison facilities is big, and it just looks needlessly complicated. What is the problem with that? Think of it this way: I wanted to talk about *algorithms* and solving real problems .. like those are the best uses of lecture time and examples. If the language injects something that you feel is not such a great use of time, you notice that those minutes are taken away from the actual course goals.
Now for Python, == vs. is nothing like that bad. Both == and is are sensible, necessary parts of the language, I would just prefer to talk about == earlier and is later. Of the two, == is the no-brainer one when the students just have ints and strings, and no-brainer is what you want in week 2.
For teaching, you are not just identifying the 500 important things they need to know. To teach artfully, you are trying to think of an ordering, layering on 50 new things each week, where the subest achieved each week is internally coherent and you can do a project just using that subset, and at the end of the course you've gotten through everything.
Best,
Nick
A couple people have said this but I'll ask again because I am curious: would it be possible to delay introduction of None entirely until it's time to introduce is?
The biggest thing None seems to be needed for beginners is to learn the idiom for function signature default arguments for mutable parameters (the classic "Least Astonishment" and the Mutable Default Argument problem <https://stackoverflow.com/questions/1132941/least-astonishment-and-the-mutable-default-argument> ).
If the goal of the course is to get as quickly as possible to *algorithms* and solving real problems, then I don't see why you even need to teach default arguments at all.
--- Ricky.
"I've never met a Kentucky man who wasn't either thinking about going home or actually going home." - Happy Chandler
Nick. I think you should realize that I—and a number of the other people who dislike your elliding the difference between equality and identity—also teach beginning programmers.
1. You could use "is", and say "It's most proper to use "is" when comparing to certain values, including None which we will explain later. For now,you just need to know that "is" plays a role similar to == here - testing if the thing is None." That would be fine. "I'm going to explain that later" is not that rare a thing to say in this course.
YES. This is the obvious and correct approach.
2. You could just use == like you do with strings and ints, and mention "it's more proper to use "is" with None here, and we'll start doing that when we get to "is" organically."
If you don't forget that footnote every time you show code, I guess this is a less-good, but not horrible, approach.
On Wed, Sep 1, 2021 at 12:22 PM Nick Parlante <nick@cs.stanford.edu> wrote:
Or put another way, None is a pretty core Python topic, so there's no sense in dancing around it too much. Really "is" is the problem.
Indeed, that's quite true. However, while students will see None very early on in their Python experience -- likely from failing to put a return statement in a function definition. But they probably don't need to actually compare to it early in their coding experience. 1. You could use "is", and say "It's most proper to use "is" when comparing
to certain values, including None which we will explain later.
yup -- a fine way to introduce it when you need to -- which again, may not be so early in the class. Interestingly, I find I use "is" far more in class than in real code -- because I use it to talk about the concept of object identity, etc -- that is, David Mertz's example of the difference between: x = y = [] and x, y, = [], [] So yes -- talking about "is: is very closely tied to talking about the concepts of object identity, mutability, all of that. BTW,, I think "is" works best about what you get to lists - so then you
have copies and identity and equality are meaningful, but there's a lot of more basic stuff to get through before you get to lists. Like you don't want to do lists before you have loops and logic.
you don't? the most basic loops in Python are for loops, which loop through an iterable, of which the most common are lists :-) I certainly don't want to teach about the whole iteration protocol before i talk about lists :-) Though yes, you don't need to get into the mutable aspects of lists right off the bat. The is a real chicken and egg challenge with teaching -- but my philosophy is to never demonstrate or teach a "bad practice" as a way to avoid having to talk about a topic early. better to ay (as you suggest above) "trust me on this, it will all make sense later" -CHB PS: Another note: my intro to Python class is an introduction to Python, but NOT an introduction to programming -- we expect our students to have some experience with some programming language. So I do dive into things like object identity pretty early on, as it is something fairly unique to Python. -- Christopher Barker, PhD (Chris) Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
On 2021-09-01 10:28, Nick Parlante wrote:
Now for Python, == vs. is nothing like that bad. Both == and is are sensible, necessary parts of the language, I would just prefer to talk about == earlier and is later. Of the two, == is the no-brainer one when the students just have ints and strings, and no-brainer is what you want in week 2.
Then go ahead and do that! :-) No one and nothing is stopping you from teaching your class in any manner you choose. It's just that you seem to have chosen a particular combination of IDE features, teaching schedule, and pedagogical desiderata that has painted you into a corner where you cannot tell the students what you want to tell them when you want to tell it to them without their IDE giving them some kind of warning. I just don't see how any of that is a Python problem at all. There is nothing wrong, in my view, with a teaching approach that tells people to do a thing one way and then later tells them to move on to a different way. When you learn subtraction in first or second grade, they tell you that if you see something like 2 - 5, you "can't do that" because you "can't" take a bigger number away from a smaller one. Then later they tell you that actually you can, you just get a negative number. Similarly, I don't see any problem with telling your students about ==, using == None, and then later telling them that actually there's a better way. Or you can tell them from the beginning to always write `is None` instead of `== None`, without explaing why, but just tell them to bear with you because you'll explain the reasoning later.
For teaching, you are not just identifying the 500 important things they need to know. To teach artfully, you are trying to think of an ordering, layering on 50 new things each week, where the subest achieved each week is internally coherent and you can do a project just using that subset, and at the end of the course you've gotten through everything.
That's right, but the key word is "trying". You won't always achieve 100% internal consistency at every moment in the class --- and that's fine! More importantly, those decisions are wholly decisions to be made be the teacher. These are issues about how to teach Python, not about what Python actually is. The fact that `is` and `==` are two different comparison operators is part of how Python works. There isn't any solution here that involves eliminating that difference. It's your choice as a teacher how to handle it, but I just don't see how Python (or PEP 8) needs to change here. As someone who has taught Python a bit, I'd say if `== None` vs `is None` is a major issue for you, your class is probably pretty awesome already, because that's a pretty minor issue! As long as, by the end of the class, you wind up conveying the essential concepts about equality, identity, and how to use them, I think you'll have succeeded. If there was a moment or a day or even a week in the middle where some people erroneously thought they should use `== None`, that doesn't really matter as long as they learn to use `is None` by the end of the course. Isn't that enough? -- Brendan Barnwell "Do not follow where the path may lead. Go, instead, where there is no path, and leave a trail." --author unknown
On Wed, Sep 01, 2021 at 02:33:20PM -0700, Brendan Barnwell wrote:
There is nothing wrong, in my view, with a teaching approach that tells people to do a thing one way and then later tells them to move on to a different way.
This! +1 https://en.wikipedia.org/wiki/Lie-to-children -- Steve
In fairness to Nick, he is not talking about the real world. Nick is talking about the hot-house environment of *education*, where fragile newbies are generally protected from real world issues.
Sure, but PEP8 is about the real world. Which is why I suggested that Nick might want to create a linter config (or whole new style guide) for his students. However, in my teaching, I always try to remember that my goal is not to have the students be successful in my class. But rather for them to learn what they need to know to use Python in the “real world” So if learning the “is None” idiom presents a stumbling block early on, but provides an opportunity to learn something that WILL matter in the real world, then that's a good thing. (I actually have a whole (small) lecture on "is" vs "==" as part of my curriculum) -CHB
Another idea -- have you considered deferring teaching students about None until after they've had a chance to learn about writing custom objects and operator overloading? None is primarily useful for representing the absence of some value, and I'm not sure if that's something beginners actually need to write interesting and useful code. As an example, take a look at intro courses that teach Java. Assuming they take an objects-late approach, you can stick with having students work with just primitive types (e.g. types where 'null' isn't permitted) for a fairly decent portion of the course. If you do the same in your Python course, you can defer introducing the '==' vs 'is' distinction until your students have enough background to fully understand and appreciate it. It would also arguably help them develop good habits early on: code that minimizes the use of union types (and subtyping, etc) is usually easier to maintain. -- Michael On Tue, Aug 31, 2021 at 1:08 PM Nick Parlante <nick@cs.stanford.edu> wrote:
Thanks to everyone for many thoughtful comments. I'm going to try to wrap this up.
So I talked above about how the IDEs have this very visual way of nudging beginning students to write their code to conform to PEP8, and this is the IDE default. Mostly this is a great, constructive dynamic, nudging students to follow the community standard.
As someone who spends all their time in the land of Python-beginners, I hope this little report of how the IDEs nudge the beginners can be taken as a success story by people who care about PEP8.
I should say, I'm not worried about Stanford students, they're going to be fine. In reality, I'm playing a long game here. I'm pushing for Python to grow to be the dominant intro teaching language (currently it's Java). I do not want the tooling by default to be flagging correct uses of == for, say, a highschool student a few years from now who is firing up the IDE to get started coding. Anyway, this is why I'm trying to get a little shift in the IDE default now.
I want to get a phrasing into PEP8 so I can go to PyCharm et al, and get a little shift in their default behavior. PEP8 coloring in the IDE should remain on by default. That has many fantastic qualities as it works with the beginning students. But in the default configuration, I want the PEP8 scoring to be tolerant about "== None" for non-Python-implementation code.
My original proposal was to add this parenthetical to the mandatory-is rule: "(this rule is optional for code that is not part of a Python implementation)".
Or maybe just a weak nudge to the tooling, like "(it's permissible for tooling to leave E711 off by default, though it is required for Python implementation code)". (ht Steven D'Aprano pointing out the proper numbers https://pep8.readthedocs.io/en/release-1.7.x/intro.html#error-codes)
Then I can go to PyCharm et al and say - hey, for new empty projects could you please default to PEP8 checking on but E711 off.
Is there support for such a change? The replies on this thread I would say are mostly not supportive...
Just using Todd recent reply as an example - describing == as "brittle". Then some point out that understanding equality and identity is important, so in effect this justifies getting "is" in front of the students even where == works. Then there's a practical view that being tolerant of "== None" may be a thing, but messing with PEP8 is not the way.
Is there anyone other than me who would like to push for "== None tolerant" carve out for non-Python-implementation code?
Best,
Nick
On Tue, Aug 31, 2021 at 12:19 PM <2QdxY4RzWzUUiLuE@potatochowder.com> wrote:
On 2021-08-31 at 11:15:22 -0700, Nick Parlante <nick@cs.stanford.edu> wrote:
As mentioned, PEP8 is formally for narrow cases, and includes disclaimers about not applying it mechanically, reflecting a basic reasonableness. Nothing to complain about there.
As a practical matter, the IDEs just default to having PEP8 checking on, and they do it in a very visual way - akin to a text editor which puts little squiggles under words it thinks are misspelled.
Aha: The IDEs are the zealots. :-)
(I might liken PEP8 violations to natural language grammar recommendations rather than spelling mistakes: words are either misspelled or they're not; many grammar "violations" are matters of style, individual preference, or accepted norms that change over time.)
I'm not sure I have much to add that hasn't been said already, except perhaps that your issue is, indeed, with the IDEs and the linters rather than with PEP8.
Do you tell your students which IDE(s) and linters to use, and/or how to set them up?
[...]
I want to get to a world that is, let's say, "== tolerant" ...
Do you use floating point values in your course? _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/WFXZV2... Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/3CDZCK... Code of Conduct: http://python.org/psf/codeofconduct/
Michael Lee writes:
None is primarily useful for representing the absence of some value, and I'm not sure if that's something beginners actually need to write interesting and useful code.
In Python, we generally think of writing code as a subordinate skill. :-) In reading code, beginners will need to know about None: it's going to show up over and over as a default value in function signatures and in print(foo()) output for functions without return values. You may not need to teach them about singletons, though. Steve
On Thu, Sep 02, 2021 at 04:04:40PM +0900, Stephen J. Turnbull wrote:
You may not need to teach them about singletons, though.
It's hard to teach why `is` works with None, but not with 1.234 or [], without talking about the object model and singletons. To say nothing of why it works with 0 and 1 but not 123456. -- Steve
Steven D'Aprano writes:
On Thu, Sep 02, 2021 at 04:04:40PM +0900, Stephen J. Turnbull wrote:
You may not need to teach them about singletons, though.
It's hard to teach why `is` works with None,
For definitions of "works" that comes down to "agrees with Nick that 'is' is just a weird way to spell '==' most of the time". But that's not how I think of 'is'.
but not with 1.234 or [], without talking about the object model and singletons.
Object model, of course, but singletons? AFAICS, "singleton" is a red herring here. Object model is important for '[]': there are times where it's important that 'a == b == [] and a is b', and other times where it's important that 'a == b == [] and a is not b'. But from the point of view of beginner education, at least, there's no reason why None and Ellipsis couldn't share a SpecialObjects type (although you couldn't put False and True in there).
To say nothing of why it works with 0 and 1 but not 123456.
Case in point for singletons being a red herring. True == 1 but True is not 1 (in Python 3.10). Neither object is a singleton.
On Thu, Sep 2, 2021 at 5:56 PM Stephen J. Turnbull <stephenjturnbull@gmail.com> wrote:
Steven D'Aprano writes:
On Thu, Sep 02, 2021 at 04:04:40PM +0900, Stephen J. Turnbull wrote:
You may not need to teach them about singletons, though.
It's hard to teach why `is` works with None,
For definitions of "works" that comes down to "agrees with Nick that 'is' is just a weird way to spell '==' most of the time". But that's not how I think of 'is'.
but not with 1.234 or [], without talking about the object model and singletons.
Object model, of course, but singletons? AFAICS, "singleton" is a red herring here. Object model is important for '[]': there are times where it's important that 'a == b == [] and a is b', and other times where it's important that 'a == b == [] and a is not b'. But from the point of view of beginner education, at least, there's no reason why None and Ellipsis couldn't share a SpecialObjects type (although you couldn't put False and True in there).
To say nothing of why it works with 0 and 1 but not 123456.
Case in point for singletons being a red herring. True == 1 but True is not 1 (in Python 3.10). Neither object is a singleton.
I would say "special value" rather than "singleton". True and False are a.... doubleton? You're not able to construct additional instances of that type, which is the main defining trait of the singleton. There just happen to be two of them. (Not that I *often* use "is False" or "is True", but it has been known, and certainly it's more correct than "== True".) ChrisA
On Thu, Sep 02, 2021 at 04:54:45PM +0900, Stephen J. Turnbull wrote:
Steven D'Aprano writes:
On Thu, Sep 02, 2021 at 04:04:40PM +0900, Stephen J. Turnbull wrote:
You may not need to teach them about singletons, though.
It's hard to teach why `is` works with None,
For definitions of "works" that comes down to "agrees with Nick that 'is' is just a weird way to spell '==' most of the time". But that's not how I think of 'is'.
Ah, but that's because you're a Python programmer who has been seeped in the language for many, many years :-) To most people, "is" and "equals" are synonyms, as in: * one and one is two; * two times four is eight; * the discriminant of a general quadratic a x^2 + bx + c is b^2-4ac etc. And there are programming languages where `is` *is* an equality test, e.g. Hypertalk lets you say things like: if x + 1 is 2 then ... if the first word of the last line of text is "Goodbye" then ... So for somebody who is expecting "is" to be a synonym for `==`, how do you explain what it actually is? You need to talk about the Python execution model, how all values are objects, and that the reason that `x is None` always does the right thing but `x is []` doesn't is that None is a singleton, so that there is only one object that is None, but the empty list is not, there are many, many distinct empty lists. You might be able to avoid using the word "singleton", but it would be difficult, and even more difficult would be to avoid using the *concept* of a singleton. Otherwise, you leave open the possibility that there could be *two* None objects, and that there might be some unusual circumstances where the identity comparison will return False: Q: Will `None is None` always return true? A: Yes, None is identical to None. Q: How about `eval('None') is None`? A: Yes, `eval('None')` is None too. Q: What about `type(None)() is None`? A: Yes, that's also None. Q: How about `builtins.__dict__['None'] is None`? A: Yes, that is also None. Q: If I have ctypes return None, is that also identical to None? A: Yes it is. Q: What if I pickle None from one version of Python into another? A: Yes, that is still identical to None. Q: Suppose I use marshal instead of pickle? A: Yes, that is still identical to None. Q: But suppose I ... A: Look, there's only ever one None object, no matter where it comes from! Q: Oh, its a singleton, why didn't you say so? *wink* -- Steve
On Fri, Sep 3, 2021 at 3:24 PM Steven D'Aprano <steve@pearwood.info> wrote:
On Thu, Sep 02, 2021 at 04:54:45PM +0900, Stephen J. Turnbull wrote:
Steven D'Aprano writes:
On Thu, Sep 02, 2021 at 04:04:40PM +0900, Stephen J. Turnbull wrote:
You may not need to teach them about singletons, though.
It's hard to teach why `is` works with None,
For definitions of "works" that comes down to "agrees with Nick that 'is' is just a weird way to spell '==' most of the time". But that's not how I think of 'is'.
Ah, but that's because you're a Python programmer who has been seeped in the language for many, many years :-)
To most people, "is" and "equals" are synonyms, as in:
* one and one is two; * two times four is eight; * the discriminant of a general quadratic a x^2 + bx + c is b^2-4ac
etc. And there are programming languages where `is` *is* an equality test, e.g. Hypertalk lets you say things like:
if x + 1 is 2 then ...
if the first word of the last line of text is "Goodbye" then ...
So for somebody who is expecting "is" to be a synonym for `==`, how do you explain what it actually is? You need to talk about the Python execution model, how all values are objects, and that the reason that `x is None` always does the right thing but `x is []` doesn't is that None is a singleton, so that there is only one object that is None, but the empty list is not, there are many, many distinct empty lists.
Yes, although I'd love to have a good term for "there is only ever one object of this type AND VALUE", rather than "there is only one object of this type", so that True and False could be discussed the same way as singletons. String interning behaves the same way too, but I don't want to say that None is an "inherently interned type" or anything ridiculous like that! Still, the two comparison operators "is" and "==" in Python are a lot easier to explain than "==" and "===" in JavaScript. (Or, worse, PHP, where two strings will be compared as numbers by ==.) In Python, it's simple: "==" tests if they have equal value, and "is" tests if they're the same object. And since it is that simple, you HAVE to have a concept in your head of what it means to be "the same object", which comes down to the object and reference model. ChrisA
On Fri, Sep 03, 2021 at 03:43:03PM +1000, Chris Angelico wrote:
Yes, although I'd love to have a good term for "there is only ever one object of this type AND VALUE", rather than "there is only one object of this type", so that True and False could be discussed the same way as singletons.
I believe that the "official" Design Pattern name for something that generalisations the Singleton pattern to two or more instances is the Multiton: https://en.wikipedia.org/wiki/Multiton_pattern but most people just use the term "singleton" in the sense that there is only a single True and a single False. Or sometimes "dupleton" or "doubleton". Another term sometimes used is "flyweight", which is also used for interned strings and ints. https://python-patterns.guide/gang-of-four/flyweight/ Interestingly, the term "singleton" was used in Python *before* the Gang Of Four book used it: https://python-patterns.guide/gang-of-four/singleton/ What would you call a class that has *no* instances? Obviously that would only be useful in a language where classes are first-class citizens. Why create a singleton instance if you can just use the class object itself? -- Steve
On Fri, Sep 3, 2021 at 7:35 PM Steven D'Aprano <steve@pearwood.info> wrote:
On Fri, Sep 03, 2021 at 03:43:03PM +1000, Chris Angelico wrote:
Yes, although I'd love to have a good term for "there is only ever one object of this type AND VALUE", rather than "there is only one object of this type", so that True and False could be discussed the same way as singletons.
I believe that the "official" Design Pattern name for something that generalisations the Singleton pattern to two or more instances is the Multiton:
https://en.wikipedia.org/wiki/Multiton_pattern
but most people just use the term "singleton" in the sense that there is only a single True and a single False. Or sometimes "dupleton" or "doubleton".
Another term sometimes used is "flyweight", which is also used for interned strings and ints.
Fair enough. I'll try to remember that one. Useful term, although a little less than intuitive.
What would you call a class that has *no* instances? Obviously that would only be useful in a language where classes are first-class citizens. Why create a singleton instance if you can just use the class object itself?
Based on the way Java seems to use it, probably "namespace" would be the best term. Effectively, a class that never gets instantiated is a storehouse for static methods, which means it's basically what Python would do with a module full of top-level functions. ChrisA
Steven D'Aprano writes:
Ah, but that's because you're a Python programmer who has been seeped in the language for many, many years :-)
No, it's because I'm a Lisp programmer for twice as long, and know the differences among #'=, #'string=, #'equal, #'eql, and #'eq (the last is `is'`.
To most people, "is" and "equals" are synonyms,
In Python, this is a *bug*, and if it's not fixed, they are going to have a lot of trouble with mutable containers. Especially as default values for function arguments.
So for somebody who is expecting "is" to be a synonym for `==`, how do you explain what it actually is? You need to talk about the Python execution model,
I guess you need this if you want to explain how "x == None" can fail to do what you want, but that's independent of None's uniqueness.
how all values are objects, and that the reason that `x is None` always does the right thing
TOOWTDI violation. Python has both `==` and `is` *because* they do different things. What each does is a right thing. The programmer's problem is to learn what each does and when to use it.
but `x is []` doesn't is that None is a singleton, so that there is only one object that is None, but the empty list is not, there are many, many distinct empty lists.
I would avoid use of "singleton" personally, since it's also used to denote a *type* with only one member, and for many students (eg, all of mine) you would have to define it. I don't see what "None is a singleton, so that" adds to You need to talk about the Python execution model, how all values are objects, and that the reason that `x is None` always does the right thing but `x is []` doesn't is that there is only one object that is None, but the empty list is not, there are many, many distinct empty lists.
You might be able to avoid using the word "singleton",
I just did, or maybe I should say, you just did. ;-) Steve
This has gotten a bit OT.. Is there’s mailing list or something for folks to discuss the teaching of Python? I can’t seem to find one. But for now: You need to talk about the Python execution model, how all values
are objects,
When you say “execution model” it sound advanced. But if people are to understand Python at all, they really need to know the basics: Names vs Values Identity vs equality Mutable vs immutable There is also the question of whether you are teaching Python to folks that have some programming experience or teaching introductory programming to complete newbies. I suspect the OP is doing the latter, and there is something to be said for getting folks used to general programming concepts before getting into the Python specifics. An aside: checking identity (using is) is pretty rare in production code, outside of “is None” ( and module-specific singletons) beginners will get pretty darn far if they only know “is” in that context. Kind of like, as Chris A suggested, how beginners will only know “with” in the context of opening files, and don’t need to understand the concept of context managers in general. What I do is tell students they should use “is None” and then demonstrate failure of == None with numpy arrays to make the point (I’m not teaching numpy) In that case, they are not so much learning about object identity as they are operator overloading—another topic that needs to be covered eventually. -CHB
--
Christopher Barker, PhD (Chris) Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
On Fri, Sep 3, 2021 at 2:05 PM Christopher Barker <pythonchb@gmail.com> wrote:
This has gotten a bit OT..
Is there’s mailing list or something for folks to discuss the teaching of Python? I can’t seem to find one.
In theory, there is edu-sig (
https://www.python.org/community/sigs/current/edu-sig/) but it is essentially dead. André Roberge
André Roberge writes:
In theory, there is edu-sig ( https://www.python.org/community/sigs/current/edu-sig/) but it is essentially dead.
Don't they still have a summit at PyCon every year? Surely they're discussing somewhere! Steve
On Tue, Aug 31, 2021 at 2:18 PM Nick Parlante <nick@cs.stanford.edu> wrote:
Hi Steven, I wish I could blame this on zealots, so let me talk a little bit about how PEP8 works in education, and I'll talk about moving forward in another message.
As mentioned, PEP8 is formally for narrow cases, and includes disclaimers about not applying it mechanically, reflecting a basic reasonableness. Nothing to complain about there.
As a practical matter, the IDEs just default to having PEP8 checking on, and they do it in a very visual way - akin to a text editor which puts little squiggles under words it thinks are misspelled.
This maybe sounds annoying, but looking at how people learn to code, it's actually fantastic, and a giant success for the goals of PEP8. Instead of spending lecture time at some point reviewing rules about spacing or whatever, the students are absorbing that stuff just imperceptibly as the IDE is nudging them the right way by putting little colored marks around code it doesn't like. PEP8 contains a bunch of conventions, and now you have this crop of junior programmers who have absorbed the important ones, thus avoiding future pointless bikeshed wars.
I hope people who care about PEP8 can have a moment of satisfaction, appreciating how IDEs have become a sort of gamified instrument to bring PEP8 to the world at low cost. So that's the good news.
The visual quality that makes the IDEs so effective, also loses all the nuance. I have tried to explain to the students this or that they can let slide, but as a matter of human nature, it's hopeless. They want to get rid of the IDE marked code. And keep in mind, their devotion to that is mostly a force for good.
So this basically great, but rather inflexible dynamic of the students, the IDEs, and PEP8 is what brings me here.
I want to get to a world that is, let's say, "== tolerant". I claim you can teach a nice intro course using == early in the course, and many people have chimed in that they feel there's value in "is". I want to work out a solution between PEP8 and the IDEs where both approaches are permitted.
Let me write up some proposals in a separate message, and people can comment on those.
Best,
Nick
Before we can start talking about solutions, we need to first establish there is an actual problem to be solved to begin with. Nobody who has commented here agrees with you on that. So you need to convince people this is actually something that needs to be fixed in the first place before anyone will consider any fixes. Using "==" is a brittle approach that will only work if you are lucky and only get certain sort of data types. "is" works no matter what. The whole point of Python in general and pep8 in particular is to encourage good behavior and discourage bad behavior, so you have an uphill battle convincing people to remove a rule that does exactly that. People learning Python MUST know the difference between equality and identity. It is one of the most central aspects of the language, and they WILL write horribly broken code if they don't, even at a beginner level. So we are talking about an issue that will only ever slightly and briefly surprise the very most beginning students, assuming they have an IDE that marks pep8 violations and assuming their lecturers don't just tell them to turn that feature off until it is needed or give them a config file to change it. That is not sufficient justification to throw away a good rule that exists for a good reason. So my proposal is for you to treat it as a learning opportunity for your students rather than an obstacle. They need to learn about the distinction between equality and identity, ideally sooner rather than later, and this is a great jumping-off point for that.
On Tue, Aug 31, 2021 at 11:15:22AM -0700, Nick Parlante wrote:
As a practical matter, the IDEs just default to having PEP8 checking on, and they do it in a very visual way - akin to a text editor which puts little squiggles under words it thinks are misspelled.
This maybe sounds annoying, but looking at how people learn to code, it's actually fantastic, and a giant success for the goals of PEP8.
You have convinced me that having IDEs run linters by default is a great advantage to both educators and students. But I'd like to point out that, good as it may be, it is not a success for the goals of PEP-8 because PEP-8 isn't about teaching newbies to code. It's more of an unexpected bonus. We're unlikely to change PEP-8 to recommend equality checks with None. For non-student experienced Python programmers, `is None` is the correct way to check for None. I suggest you approach the people who write IDEs are make a stong case for "education mode" where `== None` checks are disabled. If there are IDEs that sell to the education market, they may jump on board the idea. -- Steve
I think the provenance of the "is None" rule comes from before None was a guaranteed singleton. In older versions of Python, it was *possible* to instantiate more instances of None. So it was possible for "x == None" to fail even if x was *a* None. While the community is very used to and invested in the style, from a practical perspective I don't think "x == None" is necessary wrong anymore. On Mon, Aug 30, 2021 at 2:45 PM Nick Parlante <nick@cs.stanford.edu> wrote:
Hi there python-ideas - I've been teaching Python as a first programming language for a few years, and from that experience I want to propose a change to PEP8. I'm sure the default position for PEP8 is to avoid changing it. However, for this one rule I think a good case can be made to make it optional, so let me know what you think.
Let me start with what I've learned from teaching students in Java and now in Python. In Java, you use == for ints, but you need to use equals() for strings. Of course students screw this up constantly, using == in a context that calls for equals() and their code does not work right. Then for Java arrays a different comparison function is required, and so it goes. To teach comparisons in Python, I simply say "just use ==" - it works for ints, for strings, even for lists. Students are blown away by how nice and simple this is. This is how things should work. Python really gets this right.
So what is the problem?
The problem for Python is what I will call the "mandatory-is" rule in PEP8, which reads:
Comparisons to singletons like None should always be done with is or is not, never the equality operators.
For the students, this comes up in the first week of the course with lines like "if x == None:" which work perfectly with == but should use is/is-not for PEP8 conformance.
My guess is that this rule is in PEP8 because, within a Python implementation, it is within the programmer's mental model that, say, False is a singleton. The mandatory-is rule is in PEP8 to reinforce that mental model by requiring the is operator. Plus it probably runs a tiny bit faster.
However, for "regular" Python code, not implementing Python, forcing the use of is instead of the simpler == is unneeded and unhelpful (and analogously forcing "is not" when != works correctly). What is the benefit of forcing the is operator there? I would say it spreads an awareness of the details of how certain values are allocated within Python. That's not much of a benefit, and it's kind of circular. Like if programmers were permitted to use ==, they wouldn't need to know the details of how Python allocates those values. Being shielded from implementation details is a Python strength - think of the Java vs. Python story above. Is Java better because it builds an awareness in the programmer of the different comparison functions for different types? Of course not! Python is better in that case because it lets the programmer simply use == and not think about those details. Understanding the singleton strategy is important in some corners of coding, but forcing the is operator on all Python code is way out of proportion to the benefit.
As a practical matter, the way this comes up for my students is that IDEs by default will put warning marks around PEP8 violations in their code. Mostly this IDE-coaching is very helpful for students learning Python. For example, It's great that beginning Python programmers learn to put one space around operators right from the first day. Having taught thousands of introductory Python students, the one PEP8 rule that causes problems is this mandatory-is rule.
As a teacher, this is especially jarring since the "just use ==" rule is so effortless to use correctly. In contrast, the mandatory-is rule adds a little pause where the programmer should think about which comparison operator is the correct one to use. It's not hard, but it feels unnecessary.
As a contrasting example, in the language C, programmers need to understand == vs. is right from the first day. You can't get anything done in C without understanding that distinction. However that is just not true for regular (not-Python-implementation) Python code, where == works correctly for the great majority of cases.
Here is my proposal:
Add the following parenthetical to the mandatory-is rule: (this rule is optional for code that is not part of an implementation of Python).
So in effect, programmers outside of a Python implementation can choose to use == or is for the "if x == None:" case. In this way, PEP8 conforming code before the change is still conforming. Moving forward, I would expect that regular code will trend towards using == in such a case, reserving is for the rare cases where it is needed for correctness.
PEP8 was originally just for Python implementations, so why is this change needed? Because as a practical matter, the vast majority of code that is using PEP8 is not part of a Python implementation. This may not have been the original mission of PEP8, but it is how things have worked out.
Now we are in a situation where the rules in PEP8 are sent out to this ocean of Python programmers of many different ability levels writing regular code that is not a Python implementation. One could imagine a separate PEP800 style guide for regular code, but we don't need to do that, because in almost all cases PEP8 works great for regular code. I have taught thousands of new Python programmers, and the only place where PEP8 serves them poorly is this mandatory-is rule. Therefore instead of a separate style guide for regular code, I propose an exception for this one problem rule.
Ultimately this comes down to the question - should PEP8 push regular, not-Python-implementation code to use is for singletons in cases where == works perfectly? Seeing how effortless it is for programmers to use == as their first choice, I think PEP8 should allow that practice.
Best,
Nick _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/JWLKBT... Code of Conduct: http://python.org/psf/codeofconduct/
-- CALVIN SPEALMAN SENIOR QUALITY ENGINEER calvin.spealman@redhat.com M: +1.336.210.5107 [image: https://red.ht/sig] <https://red.ht/sig> TRIED. TESTED. TRUSTED. <https://redhat.com/trusted>
On Aug 31, 2021, at 9:52 AM, Calvin Spealman <cspealma@redhat.com> wrote:
I think the provenance of the "is None" rule comes from before None was a guaranteed singleton. In older versions of Python, it was possible to instantiate more instances of None. So it was possible for "x == None" to fail even if x was *a* None.
While the community is very used to and invested in the style, from a practical perspective I don't think "x == None" is necessary wrong anymore.
Unless x is a type that redefines == like numpy does. Then you don’t get the answer you might expect.
On Mon, Aug 30, 2021 at 2:45 PM Nick Parlante <nick@cs.stanford.edu> wrote: Hi there python-ideas - I've been teaching Python as a first programming language for a few years, and from that experience I want to propose a change to PEP8. I'm sure the default position for PEP8 is to avoid changing it. However, for this one rule I think a good case can be made to make it optional, so let me know what you think.
Let me start with what I've learned from teaching students in Java and now in Python. In Java, you use == for ints, but you need to use equals() for strings. Of course students screw this up constantly, using == in a context that calls for equals() and their code does not work right. Then for Java arrays a different comparison function is required, and so it goes. To teach comparisons in Python, I simply say "just use ==" - it works for ints, for strings, even for lists. Students are blown away by how nice and simple this is. This is how things should work. Python really gets this right.
So what is the problem?
The problem for Python is what I will call the "mandatory-is" rule in PEP8, which reads:
Comparisons to singletons like None should always be done with is or is not, never the equality operators.
For the students, this comes up in the first week of the course with lines like "if x == None:" which work perfectly with == but should use is/is-not for PEP8 conformance.
My guess is that this rule is in PEP8 because, within a Python implementation, it is within the programmer's mental model that, say, False is a singleton. The mandatory-is rule is in PEP8 to reinforce that mental model by requiring the is operator. Plus it probably runs a tiny bit faster.
However, for "regular" Python code, not implementing Python, forcing the use of is instead of the simpler == is unneeded and unhelpful (and analogously forcing "is not" when != works correctly). What is the benefit of forcing the is operator there? I would say it spreads an awareness of the details of how certain values are allocated within Python. That's not much of a benefit, and it's kind of circular. Like if programmers were permitted to use ==, they wouldn't need to know the details of how Python allocates those values. Being shielded from implementation details is a Python strength - think of the Java vs. Python story above. Is Java better because it builds an awareness in the programmer of the different comparison functions for different types? Of course not! Python is better in that case because it lets the programmer simply use == and not think about those details. Understanding the singleton strategy is important in some corners of coding, but forcing the is operator on all Python code is way out of proportion to the benefit.
As a practical matter, the way this comes up for my students is that IDEs by default will put warning marks around PEP8 violations in their code. Mostly this IDE-coaching is very helpful for students learning Python. For example, It's great that beginning Python programmers learn to put one space around operators right from the first day. Having taught thousands of introductory Python students, the one PEP8 rule that causes problems is this mandatory-is rule.
As a teacher, this is especially jarring since the "just use ==" rule is so effortless to use correctly. In contrast, the mandatory-is rule adds a little pause where the programmer should think about which comparison operator is the correct one to use. It's not hard, but it feels unnecessary.
As a contrasting example, in the language C, programmers need to understand == vs. is right from the first day. You can't get anything done in C without understanding that distinction. However that is just not true for regular (not-Python-implementation) Python code, where == works correctly for the great majority of cases.
Here is my proposal:
Add the following parenthetical to the mandatory-is rule: (this rule is optional for code that is not part of an implementation of Python).
So in effect, programmers outside of a Python implementation can choose to use == or is for the "if x == None:" case. In this way, PEP8 conforming code before the change is still conforming. Moving forward, I would expect that regular code will trend towards using == in such a case, reserving is for the rare cases where it is needed for correctness.
PEP8 was originally just for Python implementations, so why is this change needed? Because as a practical matter, the vast majority of code that is using PEP8 is not part of a Python implementation. This may not have been the original mission of PEP8, but it is how things have worked out.
Now we are in a situation where the rules in PEP8 are sent out to this ocean of Python programmers of many different ability levels writing regular code that is not a Python implementation. One could imagine a separate PEP800 style guide for regular code, but we don't need to do that, because in almost all cases PEP8 works great for regular code. I have taught thousands of new Python programmers, and the only place where PEP8 serves them poorly is this mandatory-is rule. Therefore instead of a separate style guide for regular code, I propose an exception for this one problem rule.
Ultimately this comes down to the question - should PEP8 push regular, not-Python-implementation code to use is for singletons in cases where == works perfectly? Seeing how effortless it is for programmers to use == as their first choice, I think PEP8 should allow that practice.
Best,
Nick _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/JWLKBT... Code of Conduct: http://python.org/psf/codeofconduct/
-- CALVIN SPEALMAN SENIOR QUALITY ENGINEER calvin.spealman@redhat.com M: +1.336.210.5107
TRIED. TESTED. TRUSTED. _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/CE2USF... Code of Conduct: http://python.org/psf/codeofconduct/
There doesn't SEEM to be any way to get a non-singleton None in Python 1.0 % python Python 1.0.1 (Jul 15 2016) Copyright 1991-1994 Stichting Mathematisch Centrum, Amsterdam
x = None type(x) <type 'None'> id(None) 6587488 y = None x is y 1 id(x) 6587488
Restarting the interpreter, I even get the same id(): % python Python 1.0.1 (Jul 15 2016) Copyright 1991-1994 Stichting Mathematisch Centrum, Amsterdam
id(None) 6587488
There might be some weird and clever way to get a non-unique None out of Python 1.0. But it's not anything obvious. Btw. The date given isn't the release date, obviously. It's when Anaconda Inc. made a package of 1.0.1 as a cute gesture on conda-forge. But they just did it with the original source code, it's not enhanced functionally. On Tue, Aug 31, 2021 at 9:51 AM Calvin Spealman <cspealma@redhat.com> wrote:
I think the provenance of the "is None" rule comes from before None was a guaranteed singleton. In older versions of Python, it was *possible* to instantiate more instances of None. So it was possible for "x == None" to fail even if x was *a* None.
While the community is very used to and invested in the style, from a practical perspective I don't think "x == None" is necessary wrong anymore.
On Mon, Aug 30, 2021 at 2:45 PM Nick Parlante <nick@cs.stanford.edu> wrote:
Hi there python-ideas - I've been teaching Python as a first programming language for a few years, and from that experience I want to propose a change to PEP8. I'm sure the default position for PEP8 is to avoid changing it. However, for this one rule I think a good case can be made to make it optional, so let me know what you think.
Let me start with what I've learned from teaching students in Java and now in Python. In Java, you use == for ints, but you need to use equals() for strings. Of course students screw this up constantly, using == in a context that calls for equals() and their code does not work right. Then for Java arrays a different comparison function is required, and so it goes. To teach comparisons in Python, I simply say "just use ==" - it works for ints, for strings, even for lists. Students are blown away by how nice and simple this is. This is how things should work. Python really gets this right.
So what is the problem?
The problem for Python is what I will call the "mandatory-is" rule in PEP8, which reads:
Comparisons to singletons like None should always be done with is or is not, never the equality operators.
For the students, this comes up in the first week of the course with lines like "if x == None:" which work perfectly with == but should use is/is-not for PEP8 conformance.
My guess is that this rule is in PEP8 because, within a Python implementation, it is within the programmer's mental model that, say, False is a singleton. The mandatory-is rule is in PEP8 to reinforce that mental model by requiring the is operator. Plus it probably runs a tiny bit faster.
However, for "regular" Python code, not implementing Python, forcing the use of is instead of the simpler == is unneeded and unhelpful (and analogously forcing "is not" when != works correctly). What is the benefit of forcing the is operator there? I would say it spreads an awareness of the details of how certain values are allocated within Python. That's not much of a benefit, and it's kind of circular. Like if programmers were permitted to use ==, they wouldn't need to know the details of how Python allocates those values. Being shielded from implementation details is a Python strength - think of the Java vs. Python story above. Is Java better because it builds an awareness in the programmer of the different comparison functions for different types? Of course not! Python is better in that case because it lets the programmer simply use == and not think about those details. Understanding the singleton strategy is important in some corners of coding, but forcing the is operator on all Python code is way out of proportion to the benefit.
As a practical matter, the way this comes up for my students is that IDEs by default will put warning marks around PEP8 violations in their code. Mostly this IDE-coaching is very helpful for students learning Python. For example, It's great that beginning Python programmers learn to put one space around operators right from the first day. Having taught thousands of introductory Python students, the one PEP8 rule that causes problems is this mandatory-is rule.
As a teacher, this is especially jarring since the "just use ==" rule is so effortless to use correctly. In contrast, the mandatory-is rule adds a little pause where the programmer should think about which comparison operator is the correct one to use. It's not hard, but it feels unnecessary.
As a contrasting example, in the language C, programmers need to understand == vs. is right from the first day. You can't get anything done in C without understanding that distinction. However that is just not true for regular (not-Python-implementation) Python code, where == works correctly for the great majority of cases.
Here is my proposal:
Add the following parenthetical to the mandatory-is rule: (this rule is optional for code that is not part of an implementation of Python).
So in effect, programmers outside of a Python implementation can choose to use == or is for the "if x == None:" case. In this way, PEP8 conforming code before the change is still conforming. Moving forward, I would expect that regular code will trend towards using == in such a case, reserving is for the rare cases where it is needed for correctness.
PEP8 was originally just for Python implementations, so why is this change needed? Because as a practical matter, the vast majority of code that is using PEP8 is not part of a Python implementation. This may not have been the original mission of PEP8, but it is how things have worked out.
Now we are in a situation where the rules in PEP8 are sent out to this ocean of Python programmers of many different ability levels writing regular code that is not a Python implementation. One could imagine a separate PEP800 style guide for regular code, but we don't need to do that, because in almost all cases PEP8 works great for regular code. I have taught thousands of new Python programmers, and the only place where PEP8 serves them poorly is this mandatory-is rule. Therefore instead of a separate style guide for regular code, I propose an exception for this one problem rule.
Ultimately this comes down to the question - should PEP8 push regular, not-Python-implementation code to use is for singletons in cases where == works perfectly? Seeing how effortless it is for programmers to use == as their first choice, I think PEP8 should allow that practice.
Best,
Nick _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/JWLKBT... Code of Conduct: http://python.org/psf/codeofconduct/
--
CALVIN SPEALMAN
SENIOR QUALITY ENGINEER
calvin.spealman@redhat.com M: +1.336.210.5107 [image: https://red.ht/sig] <https://red.ht/sig> TRIED. TESTED. TRUSTED. <https://redhat.com/trusted> _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/CE2USF... Code of Conduct: http://python.org/psf/codeofconduct/
-- Keeping medicines from the bloodstreams of the sick; food from the bellies of the hungry; books from the hands of the uneducated; technology from the underdeveloped; and putting advocates of freedom in prisons. Intellectual property is to the 21st century what the slave trade was to the 16th.
On Tue, Aug 31, 2021 at 09:49:36AM -0400, Calvin Spealman wrote:
I think the provenance of the "is None" rule comes from before None was a guaranteed singleton. In older versions of Python, it was *possible* to instantiate more instances of None.
I don't suppose you can show how that works? I've got Python 1.5 and Python 0.9 (!) installed and I can't find any way to get a second instance. Python 0.9 doesn't even allow calling the type: >>> type(None)() Unhandled exception: type error: call of non-function Stack backtrace (innermost last): File "<stdin>", line 1 Comparison operators don't even work on their own, you need to put them in an if statement: >>> a = None >>> b = eval('None') >>> a is b Parsing error: file <stdin>, line 1: a is b ^ Unhandled exception: run-time error: syntax error >>> if a is b: ... print 'true' ... true -- Steve
Can you redefine the name, “None” in ancient Python? -CHB -- Christopher Barker, PhD (Chris) Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
On Wed, Sep 1, 2021, 11:55 AM Christopher Barker <pythonchb@gmail.com> wrote:
Can you redefine the name, “None” in ancient Python?
I didn't show it in my earlier post, but in Python 1.0.1, you an indeed rebind the name None (much as you could True/False until 2.4 or something... I probably have the version wrong in my guess). Nonetheless, the None *object* is a singleton, and it's own type. I think the concept of NoneType was relatively late, like in the 2.x series.
-CHB
-- Christopher Barker, PhD (Chris)
Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/B2WPDP... Code of Conduct: http://python.org/psf/codeofconduct/
On Wed, Sep 01, 2021 at 12:07:52PM -0400, David Mertz, Ph.D. wrote:
On Wed, Sep 1, 2021, 11:55 AM Christopher Barker <pythonchb@gmail.com> wrote:
Can you redefine the name, “None” in ancient Python?
I didn't show it in my earlier post, but in Python 1.0.1, you an indeed rebind the name None (much as you could True/False until 2.4 or something... I probably have the version wrong in my guess).
As late as 1.5, you could still rebind the name None.
Nonetheless, the None *object* is a singleton, and it's own type. I think the concept of NoneType was relatively late, like in the 2.x series.
There always was a NoneType, although it wasn't always called NoneType. In 1.5 and 0.9 it was called 'None', although that wasn't as confusing as you might think, because you couldn't do anything with it, it wasn't callable and had no methods. >>> type(None) <type 'None'> In 1.5 it was also available via the `types` module, under the alias NoneType. -- Steve
participants (20)
-
2QdxY4RzWzUUiLuE@potatochowder.com
-
André Roberge
-
Brendan Barnwell
-
Calvin Spealman
-
Carl Meyer
-
Chris Angelico
-
Christopher Barker
-
David Mertz, Ph.D.
-
Marc-Andre Lemburg
-
Matthias Bussonnier
-
Michael Lee
-
Nick Parlante
-
Oscar Benjamin
-
Paul Moore
-
Richard Damon
-
Ricky Teachey
-
Stephen J. Turnbull
-
Steven D'Aprano
-
Thomas Grainger
-
Todd