Python does not usually use explicit line-ending characters; the parser can almost always figure out where a statement ends from the positioning of newlines. However, Python's syntax allows you to use a semicolon to end a statement instead, which allows you to place multiple statements on a single line. I have (almost) never used this feature, and I can't ever recall seeing it in production code. Does the ability to end a statement with a semicolon allow you to do anything that cannot be done simply by using newlines?
6 Answers
When executing a python snippet via python -c
from bash, I find using semicolons more convenient than having to inject newlines. (In particular when the snippet has import
s, and thus cannot be done in a single statement)
-
8$\begingroup$ The same is also true of
timeit
statements (and itssetup
argument) $\endgroup$ Commented Oct 17, 2023 at 23:35
In Python specifically, the main use case is to eliminate newlines in a bit of code you are running.
The common case for this is using the -c
argument for the Python interpreter to have it run a bit of code you specify on the command line (instead of having to load it from a file). I’ve also seen it used on rare occasion with exec()
or eval()
when the code being passed in to either is relatively short, but would need multiple lines if written in ‘full form’.
The other big use for this is code golf. As an example, this:
for a in range(0, 10):b(a);c(a);d(a)
is semantically equivalent to this, but twelve bytes shorter:
for a in range(0, 10):
b(a)
c(a)
d(a)
Twelve bytes does not sound like much, but that’s a 24% savings in this case, and even for a longer program it’s pretty darn significant when golfing competitively.
There are a couple of interesting caveats to this though. The two big ones are that:
- Using a
;
like this in Python only works with compound statements (if/elif/else, for/in, while, try/except/finally/else, with, and possibly match/case, though I’ve never tried with that one). In particular, this means you can’t use a;
in Python to define a lambda expression with multiple statements in it’s body, or to have multiple statements evaluated as part of a ternary operator. - If there is no newline between the
:
in a compound statement and the first statement of the block that follows that part of the compound statement, the body of that block will continue until the next newline. This means that in my first example above, the only ways to denote the end of the loop’s body are to either insert a newline after the final statement, or have the end of the file be immediately after the final statement. This is a bit annoying for code golf, but it actually has some interesting uses, such as making it possible to write functions which have only one statement in their body all on one line (which is especially useful when writing type stubs).
-
6$\begingroup$ If you're willing to write the block on just 1 line, you're surely also willing to forego PEP-8's recommendation of 4-space indentation. If you change the block indent to just 1 space then you only save 3 bytes. But if you're golfing, I guess every byte counts. $\endgroup$– BarmarCommented Oct 18, 2023 at 14:35
-
5$\begingroup$ @Barmar or even one tab, which looks nicer $\endgroup$– Chris HCommented Oct 18, 2023 at 14:58
Similar to what abel1502 said about executing code from bash, I find myself using semicolons when using pdb
and trying to do anything involving multiple lines. It's a lot easier to deal with for one-off exploration of the state after pdb.set_trace()
and doesn't require doing interact
Ex:
import pdb
import random
def function_to_debug():
result = []
for a in range(100):
b = random.randint(1, a)
result.append((a, b))
pdb.set_trace()
# rest of the function
Then when it pauses, I might want to do some sort of exploration of stuff
(pdb) test=[]
(pdb) for i, j in result:print(i);test.append(j)
This isn't exactly a representative example, but a very simplified example of how I might want to mess around with the currently available variables when debugging.
-
$\begingroup$ Could you add an example to how you use it with pdb? I myself use it a lot like this:
import pdb; pdb.set_trace()
. But your answer makes it sound like you can do more with it? I'm curious about that. $\endgroup$– OpifexCommented Oct 18, 2023 at 9:06 -
$\begingroup$ @Opifex a more concrete/useful example doesn't come to mind right this second, but this is a very generic example of where I might want to both print one part of a tuple and save out the second element, maybe to do something else with. Personally I use
pdb.set_trace()
when exploring less-than-well-documented code I don't understand to see what the contents of variables & such actually are in practice. Hopefully this is of use though $\endgroup$ Commented Oct 18, 2023 at 9:21 -
1$\begingroup$ Ahh, I understand now. You use it inside of pdb. Not in the python code. That makes sense, thanks! $\endgroup$– OpifexCommented Oct 18, 2023 at 13:19
I used this feature when embedding Python code in an editor for a Jupyter-notebook-like experience. The code is a recipe for baking bread. It is also a template, meant to be loaded into the current context, then lightly edited to change the amounts in the first paragraph:
flour_grams = 1000
whole_wheat_percent = 45
honey_grams = 30
sourdough_starter_percent = 15
starter_is_watery = False
The rest of the script was math and boilerplate. For the code to be embedded nicely in the page, the beginning needed to be written as above, but the rest needed be minified to not take up the whole page vertically. (Eventually I had to use exec(code_str)
because standard minification was insufficient.)
This is a niche example (embedding the script in where it must be vertically compact), but I suspect a lot of different niche examples end up justifying the feature.
There have always been embedded scripting languages, and although I have never seen that use case, it was certainly a consideration even in the early days of python 2.
Other posters have mentioned bash, snippets, and GUI python, but the class is larger than that. Consider python embedded in HTML: HTML specifically ignores whitespace, and you cannot ensure that whitespace is retained in that environment.
Generally, when you embed python script inside something that is not just a text file in a file system, you have to conform to the constraints of that system.
I've always thought that the semi-colons were just included as a concession to c programmers: that's certainly the way I saw them used 20 years ago. But embedded scripting with the cpython engine was certainly an active consideration then, much more than it is today.
-
1$\begingroup$ Semicolons aren't enough to make Python work in an environment where whitespace isn't preserved, because semicolons are only for sequencing, they cannot indicate nesting depth. Consider
if a: if b: foo() else: bar()
─ you can't use semicolons to disambiguate this. $\endgroup$– kaya3Commented Oct 21, 2023 at 0:09 -
1$\begingroup$ Also isn't not really accurate to say HTML doesn't preserve whitespace; otherwise the CSS property
white-space: pre;
would be useless. Some transformations of HTML might not preserve whitespace, but those transformations are only correct when the whitespace indeed doesn't matter. $\endgroup$– kaya3Commented Oct 21, 2023 at 0:13 -
$\begingroup$ @kaya3, Complex if/else statements can be trivially decomposed into repeated If statements. HTML is just an example: I'm glad you understood the example. $\endgroup$– davidCommented Oct 21, 2023 at 0:36
-
1$\begingroup$ If you have any reference showing that either the Python designers intended semicolons to be used in environments where newlines weren't preserved, or examples of Python being embedded in HTML without whitespace-preservation in real projects, please add them to your answer. Otherwise as far as I can tell this is just false; I am aware of projects which embed Python in HTML (e.g. PyScript example) but in reality these rely on HTML preserving whitespace. $\endgroup$– kaya3Commented Oct 21, 2023 at 14:51
I've never used this feature in Python, but I am familiar with other languages that support semicolons: Algol, C, Java, etc. When I used place two or more statements on the same line, I was sending anyone reading the code a message: these two statements are tightly bound, and should be considered together. I have got out of the habit of using the idiom, perhaps because I'd tend to bind the two statements together by putting then into a single method of an object.
E.g. x=1;y=1,z=1; // They are coordinates in 3D, so they hang out together.
Compare: p = Point(x=1,y=1,z=1)
-
5$\begingroup$ In Python I'd write
x,y,z = 1,1,1
to convey that they are bound at the same time, no need for a semicolon $\endgroup$ Commented Oct 20, 2023 at 1:49
lock(); defer { unlock() }
. $\endgroup$;
and newline are both 1 byte :P $\endgroup${ consume(IF); parseIf() }
$\endgroup$;
saves on indentation characters. $\endgroup$