Why does Python 3 code run on Python 2, at all? - python

I created a small shopping list program designed to run on Python 3. I assumed my Mac was running Python 3 and tried to run the file. It seemed to work well as long as I surrounded my input strings with quotation marks, otherwise I got a NameError. This is not needed in Python 3.
Why did the code run at all? I know that functions such as print() are written differently in Python 2 so why didn't it fail from the get-go?
# List Creation
shopping_list = []
# Help
def show_help():
print("""
Add items to the list when promted.
To end the list type 'DONE'.
To view the list type 'SHOW'.
To view this help prompt, type 'HELP'.
""")
show_help()
# View
def view_list():
print(shopping_list)
while True:
new_item = input("> ")
if new_item.upper() == 'DONE':
break
elif new_item.upper() == 'HELP':
show_help()
continue
elif new_item.upper() == 'SHOW':
view_list()
continue
shopping_list.append(new_item)
print(shopping_list)

A lot of Python 3 code runs in Python 2 because Python was specifically designed to make that true.
print is a bit of a special case—simple prints of a single variable do the same thing in both languages, but more complex prints may not. How does this work?
In Python 3, print(shopping_list) is a call to the print function with a single argument, shopping_list.
In Python 2, print(shopping_list) is a print statement with one printable, the value (shopping_list), which is of course the same value as shopping_list. So you get the same thing.
Of course that isn't true as soon as you print(x, y) (which works, but prints a single 2-tuple value rather than two values in Python 2) or print(x, file=sys.stderr) (which is an error in Python 2).
input is another special case. This one wasn't designed to be compatible, you're just getting lucky here. Or maybe unlucky.
In Python 2, input() does eval(raw_input())—that is, it takes what you type and tries to evaluate it as Python source.
In Python 3, input() does what raw_input() did in Python 2—that is, it just returns a string.
So, the code sort of works in both, although you have to type "DONE" instead of just DONE in Python 2—just like you have to type "DONE" in your source code. Plus, of course, you can type __import__('os').system('rm -rf /') in Python 2 and make yourself very sad. (This is why Python 2's input doesn't exist in Python 3.)
If you're asking why Python 3 was designed this way:
Once it was decided that some backward incompatibility was required in order to fix two decades of backlogged problems, there was a discussion about whether "Python 3000" should be intentionally incompatible to noisily break everything, or as compatible as possible (but no more so) to make migration easier.
The team decided on the latter. That included making some changes to Python 2.6 to make it easier to eventually migrate to 3.x, and some design decisions in Python 3.0 that were only made to make migration from 2.6 easier.
Once Python 3 got out into the wild, that turned out to be the right choice. To almost everyone's surprise, it was actually easier to write "dual-version" code (with the help of adapter libraries like six) than to write code that could be auto-transformed via 2to3 and 3to2.
So they went even farther. Python 2.7 added a few features designed just for dual-version code, and they've continued to add things like the u string literal prefix (which does nothing in Python 3, but it's a way of making sure you have Unicode strings whether you run in 2.7 or 3.4) and bytes.__mod__ (which allows Python 2 %-formatting code to continue to work in Python 3 in cases where you've deliberately chosen to keep things in bytes—e.g., parsing HTTP, where all you know is that it's some ASCII-compatible charset until you reach the charset header).
By the way, one of those "forward-compatibility" changes to Python 2 was the addition of from __future__ import print_function, which gives you Python 3-style print.

With regards to print, Python3 made it a proper function, so you must call it with parentheses. In Python2, those parentheses are interpreted as "grouping" parenthesis rather than the start of a function call, so it makes no difference.
Python2: print (1 + 2) means, evaluate the expression (1 + 2) -> print 3
Python3: print (1 + 2) means evaluate the expression 1 + 2 and pass the result into the function print -> print(3)

It ran because it is perfectly valid Python 2 code. However, input in Python 2 tries to evaluate the string that is input as a Python expression; the equivalent in Python 3 would be eval(input("> ")). Python 2 should always use raw_input, and Python 3 enforces that by getting rid of the old input behavior and renaming raw_input to input.
As for print, each of the items following the print keyword is an expression, and a valid expression can be wrapped in parentheses. print('hello') just prints the result of the expression ('hello'), which is equivalent to the unparenthesized string 'hello'; hence, print('hello') and print 'hello' are equivalent.
Note that print 'hello', and print('hello',), however, would not be equivalent; the former prints the string hello with no line ending, while the latter prints the single-element tuple (hello,). In Python 3, print('hello',) and print('hello') would be the same, since an argument list to a function call is allowed to have a trailing comma that gets ignored.

You are not leveraging on the improvements of Python 3.
Hence it works (except with print) in Python 2

Related

Why do I get extra output from code using 'print' (or output at all, using 'return') at the REPL, but not in a script?

Suppose I make a simple script hello.py that contains just:
print('hello world')
When I run it, I just see hello world in the output. And if I try the same code at the interpreter prompt (REPL), I see the same result:
>>> print('hello world')
hello world
But if I try something more complex, like:
print('hello'), print('world')
When I run the script, I get each word on a separate line, as I expect. But at the REPL:
>>> print('hello'), print('world')
hello
world
(None, None)
What is that extra (None, None) output? Why wasn't there any extra output in the first example?
Similarly, if I try calling a function:
def example():
return 1
example()
I don't see any output, as I expect, because return is not print. But at the REPL, I do see the result:
>>> def example():
... return 1
...
>>> example()
1
This seems useful, and perhaps intentional, but I'm not sure I understand why or how it happens. I know that print is a function in 3.x. Since I'm using a function either way, could this be somehow related?
Summary
print produces the hello world output in the first case, whether from the REPL or the script; and the separate hello and world lines in the second case, again whether from the REPL or the script. The REPL itself produces the (None, None) output, because that is the result of evaluating print('hello'), print('world'). The result of evaluating just print('hello world') is None, but the REPL hides None results as a special case.
Text is displayed when it's written to the program's standard output stream. Evaluating expressions, returning from functions etc. doesn't do this automatically. print does display output (that's its purpose), and the REPL (which is outside your program) also does (except when the result is None).
That's all you really need to know; but here's some detail on how the overall system works.
How print works
In 3.x, print is a function. When you call it, it returns the special value None.
"What's None?" See What is a 'NoneType' object?.
"Why does it return that?" Because in Python, a call to a function is an expression; it has to return something1. If a function reaches the end without returning explicitly, None is what you get implicitly.
"So it doesn't return the formatted text?" No, why would it? Python style is for functions to do something as a side effect or return something other than None, not both.
"Wait, but then how can it display anything?" It doesn't need to return anything in order to display the text, and in fact return has nothing to do with displaying anything. Programs that run at the command line display their output by writing it to the standard output stream, which is something like a special file provided by the operating system.2
The REPL, and how it treats code
When you start Python without giving it a script or module to run, you get something that looks like:
Python 3.8.10 (default, Jun 22 2022, 20:18:18)
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
(The details will depend on your operating system, Python version and how it was compiled, of course.)
The tutorial in the official documentation refers to the Python executable itself as "the interpreter", and says that here we have started it "in interactive mode". In more colloquial language, the Python community typically refers to this mode, and the >>> prompt, as "the REPL".3
Blocks of code in Python (such as you might start off with if or def, and follow with some indented lines) don't evaluate to anything - not even None. They're statements, not expressions.4 But calling a function is an expression, and so it evaluates to a result which can be displayed. When you input an expression, the interpreter displays a textual representation of the result, which comes from repr.5
Except when that result is None. The repr of None is, as you might have guessed, None (that literal text, without quotes). It would be really distracting to see that every time you call print, or use .extend on a list, etc. etc. So the interpreter special-cases this, and hides the None results.
print('hello'), print('world') is an expression that makes a tuple out of the two None values from the print calls. So the result is exactly that: (None, None). That's not the special case None, so it gets displayed.6
Similarly, if we call our own function at the REPL, the result is displayed even without printing it:
>>> def example():
... return 1
...
>>> example()
1
Unless the result is None:
>>> def example():
... return None
...
>>> example()
>>>
How code behaves in a script
When you run Python code from a .py file, there is no REPL any more. So although the code is constantly evaluating expressions, there is nothing to display results - unless you do it explicitly with print. For example, you can make a test .py file that just contains 1 + 1 and run it; that's perfectly valid Python and it will compute the sum of 2 - it just won't display anything. Similarly, the example code print('hello'), print('world') computes a tuple of two None values, but doesn't display it.7
The same applies when we call our own functions: the result isn't displayed by default, unless we explicitly use something to display it (such as print). After all, it would be really annoying if we couldn't prevent the result from displaying. Real-world programs do a lot of function calling!
1Or it could raise an exception, of course.
2In Python, the standard output stream can be accessed from the sys standard library module as stdout (so, from sys import stdout and then use stdout, or import sys and then use sys.stdout). Python represents it to you as a file that's open for writing text. There is also, similarly, a standard error stream, accessible as sys.stderr, used for writing error messages. There are separate streams so that command-line programs can keep those two streams of information separate when they call each other and interpret each others' output. This design is decades old. Anyway, actually changing the pixel colours in the terminal window is done by the terminal program, not by Python. Python just says what text to display.
3That stands for "Read-Eval-Print Loop", and is an established concept seen in many programming languages. It means exactly what it sounds like: when you provide code at the REPL, the interpreter reads that code, evaluates it, and potentially prints (displays) a result. It keeps doing this, in a loop, until you exit back to the command line.
4The same is true of assignments, which is why you can't do x = (y = 1) or (x = y) = 1, even though you can do x = y = 1.
5It can't actually display an integer; it displays text that represents the integer in base ten. Integers don't have decimal (or binary, or hexadecimal...) digits. Only those representations do. It's very important for programmers to make these kinds of distinctions, in order to think clearly about problems.
6In 2.x, print was a statement (except in the latest revisions, if you enabled the forwards-compatibility options). So there was no None for the REPL to suppress, but also, code like print('hello'), print('world') wasn't possible anyway.
7I often see code - especially in Pandas examples - where someone appears to have copied and pasted from an interactive session (after figuring out what works) into a source file, leaving behind lines that just have a variable name on them (because while testing it at the REPL, the author decided to check the variable's value at that point). In a script, this is benign, but it is also useless - better to take out lines like this.
I also often see code where a list comprehension has been used to replace a for loop that is only used for its side effects. Please don't do that. It's not harmful to create the list using a list comprehension, but it's useless (you end up with a list of None values that you don't use for anything), unintuitive (list comprehensions are for creating a list; loops are for repeating a process) and could be slightly less efficient.

What was the rationale behind creating input() in Python 2?

In Python 2, there were two ways of getting input. raw_input() and input(), which was a wrapper around eval(raw_input()). In Python 3 however, input() replaced raw_input() and the old meaing of input() was deprecated. This is documented in What's new in Python 3:
PEP 3111: raw_input() was renamed to input(). That is, the new input() function reads a line from sys.stdin and returns it with the trailing newline stripped. It raises EOFError if the input is terminated prematurely. To get the old behavior of input(), use eval(input()).
But why exactly was input() around in Python 2 in the first place? What was the rationale for having user input that was evaluated as literal Python 2 code? This is what the Python 2 documentation had to say:
[input() is] Equivalent to eval(raw_input(prompt)).
This function does not catch user errors. If the input is not syntactically valid, a SyntaxError will be raised. Other exceptions may be raised if there is an error during evaluation.
If the readline module was loaded, then input() will use it to provide elaborate line editing and history features.
Consider using the raw_input() function for general input from users.
Notice the part in bold (which I emphasized). What exactly does this mean? I looked over the documentation for the readline module and found a few things. The only real relevant bit I found, however, was this:
Settings made using this module affect the behavior of both the interpreter’s interactive prompt and the prompts offered by the raw_input() and input() built-in functions.
Which doesn't really help explain why input() was created or needed in the first place, though.
Needless to say, using eval(any_user_input()) is very dangerous security wise, can cause debugging difficulties, and, from what I've read, is slow. So why did they create input() in Python 2 in the first place? Were the developers unaware at the time of the downfalls of input()?
References:
Is using eval in Python a bad practice?
eval(input()) in python 2to3
What’s New In Python 3.0
Python 2.7.12 documentation
First of all, probably the only person who can answer this question decisively is the BDFL.
input can be useful in programs that are meant to be used by a programmer, so that they can enter complex structures, like {'foo': 42}, or even expressions, but less so in programs intended to be used by an unskilled user.
From the SCM history we can see that both input and raw_input were present in 1990; or pre-0.9, when Python was in its infancy - back then exec was a function, and int('42') would have thrown an exception. Most notably, eval was already present as well, so one could have used eval(raw_input()) even back then to get much of the same effect.
Back then there was no Zen of Python yet, and the "only one obvious way" wasn't as much a guiding principle, so this could have been an oversight.
And both raw_input and input remained. During the history of Python, the backwards-compatibility was a guiding principle, so input was unchanged until backwards-incompatible Python 3 was released.
As for the bolded part about readline module: if you import readline, then you can use arrow keys to move cursor keys around on the input() line, and configurable bindings; if readline is not imported in the program, then no such behaviour exists.
Again, this wouldn't have been the reason for input existing in the first place; back in 1990 Python didn't support such editing at all, regardless of whether input or raw_input was used.
For what it worths, input builtin was there in a first available Python version (0.9.1), it is from 1991. I can imagine Python 2.x had it for backwards compatibility with Python 1.x, and Python 1.x had it for backwards compatibility with 0.x.
Say no to 0.x -> 1.x and 1.x -> 2.x porting issues!

Python 'print' statement and arguments

I'm an experienced C++ developer, but am just getting my feet wet with Python for a project at work. I'm having some basic problems and Google has been less than helpful. I suspect something about my environment is funny, but I have no clue how to diagnose it. I wrote a simple script that merely prints an argument to the screen, but I am getting an error when running it (python args.py):
Syntax Error: invalid syntax
File "args.py", line 4
print arg0
For reference, there is a carrot underneath the 0 of arg0. The script in question:
import sys
firstArg = sys.argv[0]
print firstArg
I'm sure this is something really dumb, but Python is such a foreign thing, coming from C++.
This seems pretty obvious. The traceback says you have a SyntaxError on line 4, therefore print firstArg isn't valid Python.
The not obvious part is that a lot of examples on the Internet use syntax like that. This is because in Python 2.X versions, print was a statement. Since Python 3.0, print is a function. Try print(firstArg) instead.
Protip: If you want to enable the print function in Python 2, use from __future__ import print_function.
I can understand that the print function of Python may be different for C or C++ developers because of its features.
The syntax that you use is for python 2 (now in End Of Life status). There are many differences between Python 2 and python 3.
In this case, the print was a statement in Python 2 but function in Python3.
Here is the correct syntax for print function in Python3.
print(firstArg)
Brackets are important because print is a function. The print function in Python has some parameters which make it even more powerful.

Python - I have previously defined variables yet they do not work in one function

At the very beginning of the Python Script, I have defined a lot of variables. For instance:
cmd_attack = "attack"
cmd_protect = "protect"
cmd_help = "help"
cmd_help works in a user menu function shown here:
def usermenu():
um_in=raw_input('Menu :: ')
#Help Command
if um_in.lower()==cmd_help.lower():
print(helplist)
usermenu()
That is successful - it prints the help list and then returns to the raw input. However, when I do something similar involving cmd_help in another function...
def tf_elf_battle_merc():
battleinput==raw_input('Elf :: ')
global cmd_help
global cmd_attack
global cmd_protect
if battleinput.lower()==cmd_attack.lower():
attack_merc()
elif battleinput.lower()==cmd_help.lower():
print(tf_elf_help)
That does nothing, prints no errors, and returns to the shell line - without printing anything or going anywhere. I used global commands because I was testing out possible solutions to the problem.
The order that these are put in is the CMD functions at the top, the tf_elf_battle_merc() in the middle, and the usermenu() last. I have tried a few things and the related questions haven't helped... any thoughts? I'm kind of new to Python. If you're curious, it is script where you can log in and play text-type games.
The full script is here on Pastebin.
Thank you in advance!
Edit: If you download and run the script - use "Guest" (case-sensitive) as a username and you'll be let into it
Your code (with some edits, seen below) worked fine for me after changing battleinput==raw_input('Elf :: ') to battleinput=raw_input('Elf ::'), you don't want to compare them, you want to define battleinput.
However, it should raise an error of that, since battleinput is not defined, yet you're trying to comparing it: if battleinput.lower() == ....
Also you're mixing Python 3 and Python 2? Using raw_input() from Python 2, yet print("asd") from Python 3, instead of Python 2's print "asd"?
Everything looks like your code is never reached, the problem is elsewhere.
Here's the code for Python 3, which works fine:
cmd_attack = "attack"
cmd_protect = "protect"
cmd_help = "help"
def tf_elf_battle_merc():
battleinput=input('Elf :: ') # Use raw_input() for Python 2
# You don't need the globals here
if battleinput.lower()==cmd_attack.lower():
print("attack")
elif battleinput.lower()==cmd_help.lower():
print("help")
tf_elf_battle_merc()

Python 3.2 idle : range function - print or list?

I know this is wrong thing to do, but I am using python 3 but studying it with python 2 book.
it says,
>>>range(2,7)
will show
[2,3,4,5,6]
but I know it won't show the output above, THAT I figured. so I tried:
>>>>print(range(2,7))
and ta-da- it shows follow:
range(2,7)
looks like this is the one of changes from P2 to P3 so I tried:
list(range(2,7))
this one works ok on IDLE but not ok on notepad for long coding. so finally I tried:
print(list(range(2,7)))
and it showed something similar to what I intended... Am I doing right? Is this the only way to write it?
In your IDLE case, you are running the code in IDLE's PyShell window. This is running the interactive interpreter. In interactive mode, Python interprets immediately each line you type in and it displays the value returned by evaluating the statement you typed plus anything written to standard output or standard error. For Python 2, range() returns a list and, as you discovered, in Python 3, it returns an iterable range() object which you can use to create a list object or use elsewhere in iteration contexts. The Python 3 range() is similar to Python 2's xrange().
When you edit a file in an editor like Notepad, you are writing a script file and when you run the file in the Python interpreter, the whole script is interpreted and run as a unit, even if it is only one line long. On the screen, you only see what is written to standard output (i.e. "print()") or standard error (i.e. error tracebacks); you don't see the results of the evaluation of each statement as you do in interactive mode. So, in your example, when running from a script file, if you don't print the results of evaluating something you won't see it.
The Python tutorial talks a bit about this here.
If your only goal is to get back the list representation, what you're doing is correct. Python 3.0 now treats range as returning an iterator (what xrange used to do)

Categories