2.3. Vertical Spacing¶
Vertical spacing is the placement of blank lines between lines of code. Just as a new paragraph in a book keeps sentences from forming a wall of text, vertical spacing can group certain lines of code together and separate those groups from one another.
PEP 8 has several guidelines for inserting blank lines in code: it states that you should separate functions with two blank lines, classes with two blank lines, and methods within a class with one blank line. Black automati- cally follows these rules by inserting or removing blank lines in your code, turning this code:
- NO:class ExampleClass:
- def exampleMethod1():
pass
- def exampleMethod2():
pass
- def exampleFunction():
pass
. . . into this code:
- YES: class ExampleClass:
- def exampleMethod1():
pass
- def exampleMethod2():
pass
- def exampleFunction():
pass
2.3.1. A Vertical Spacing Example¶
What Black can’t do is decide where blank lines within your functions, meth- ods, or global scope should go. Which of those lines to group together is a subjective decision that is up to the programmer.
For example, let’s look at the EmailValidator class in validators.py in the Django web app framework. It’s not necessary for you to understand how this code works. But pay attention to how blank lines separate the call() method’s code into four groups:
--snip-- def __call__(self, value): ① if not value or '@' not in value:
raise ValidationError(self.message, code=self.code)
② user_part, domain_part = value.rsplit('@', 1) ③ if not self.user_regex.match(user_part):
raise ValidationError(self.message, code=self.code)
- ④ if (domain_part not in self.domain_whitelist and
not self.validate_domain_part(domain_part)):
- # Try for possible IDN domain-part
- try:
domain_part = punycode(domain_part)
- except UnicodeError:
pass
- else:
- if self.validate_domain_part(domain_part):
return
raise ValidationError(self.message, code=self.code)
--snip--
Even though there are no comments to describe this part of the code, the blank lines indicate that the groups are conceptually distinct from each other. The first group ① checks for an @ symbol in the value parameter. This task is different from that of the second group ②, which splits the email address string in value into two new variables, user_part and domain_part . The third ③ and fourth ④ groups use these variables to validate the user and domain parts of the email address, respectively.
Although the fourth group has 11 lines, far more than the other groups, they’re all related to the same task of validating the domain of the email address. If you felt that this task was really composed of multiple subtasks, you could insert blank lines to separate them.
The programmer for this part of Django decided that the domain vali- dation lines should all belong to one group, but other programmers might disagree. Because it’s subjective, Black won’t modify the vertical spacing within functions or methods.
2.3.2. Vertical Spacing Best Practices¶
One of Python’s lesser-known features is that you can use a semicolon to separate multiple statements on a single line. This means that the following two lines:
>>> print('What is your name?')
>>> name = input()
What is your name?
Xiaobai
. . . can be written on the same line if separated by a semicolon:
>>> print('What is your name?'); name = input()
What is your name?
Laobai
As you do when using commas, you should put no space before the semicolon and one space after it.
For statements that end with a colon, such as if , while , for , def , or class statements, a single-line block, like the call to print() in this example:
>>> if name == 'Alice':
>>> print('Hello, Alice!')
. . . can be written on the same line as its if statement:
>>> if name == 'Alice': print('Hello, Alice!')
But just because Python allows you to include multiple statements on the same line doesn’t make it a good practice. It results in overly wide lines of code and too much content to read on a single line. Black splits these statements into separate lines.
Similarly, you can import multiple modules with a single import statement:
>>> import math, os, sys
Even so, PEP 8 recommends that you split this statement into one import statement per module:
>>> import math
>>> import os
>>> import sys
If you write separate lines for imports, you’ll have an easier time spot- ting any additions or removals of imported modules when you’re compar- ing changes in a version control system’s diff tool. (Version control systems, such as Git, are covered in Chapter 12.)
PEP 8 also recommends grouping import statements into the following three groups in this order:
Modules in the Python standard library, like math , os , and sys
Third-party modules, like Selenium, Requests, or Django
Local modules that are a part of the program
These guidelines are optional, and Black won’t change the formatting of your code’s import statements.