Functions and classes should have two blank lines after them, separating them from other functions and classes.
Anti-pattern
class User(object):
pass
user = User()
Best practice
class User(object):
pass
user = User()
Additional links
- https://www.python.org/dev/peps/pep-0008/#blank-lines
Expected 2 blank lines after end of function or class
Functions and classes should have two blank lines after them, separating them from other functions and classes.
Anti-pattern
class User(object):
pass
user = User()
Best practice
class User(object):
pass
user = User()
Consider the following code:
def add_function(a, b):
c = str(a) + b
print "c is %s" % c
def add_int_function(c, d):
e = c + d
print "the vaule of e is %d" % e
if __name__ =="__main__":
add_function(59906, 'kugrt5')
add_int_function(1, 2)
It always shows me: «expected 2 blank lines ,found 1» in aadd_int_function, but not in the add_function.
When I add two spaces in front of the def add_int_function(c, d):
there is a error shows unindent does not match any outer indentation level
in the end of add_function:
TT—
2,9071 gold badge26 silver badges44 bronze badges
asked Oct 27, 2016 at 3:57
2
Just add another line between your function definitions :
1 line :
2 lines:
answered Oct 27, 2016 at 8:55
Kennet CelesteKennet Celeste
4,4833 gold badges24 silver badges34 bronze badges
6
This is a pretty common question within the python community. After the release of PEP 8, new formatting styles were accepted into python. One of them states that after the definition of a class or function there must be two lines separating them. As such:
def yadayada:
print("two lines between the functions")
def secondyadayada:
print("this is the proper formatting")
So, you should never do it like:
def yadayada:
print("two lines between the functions")
def secondyadayada:
print("this is the proper formatting")
Or else PyCharm will throw that error at you.
answered Jan 27, 2019 at 15:03
shreyshreyshreyshrey
5176 silver badges20 bronze badges
0
Further clarification on @kennet-celeste & @shreyshrey ‘s answers,
Each function or class defined requires 2 spaces above and 2 spaces below. Unless the function is the last item in the script, in which the expected format is one blank line as an End of File marker. So:
# some code followed by 2 blank spaces
def function1():
def function2():
def function3():
answered Feb 7, 2022 at 11:10
M_MercilessM_Merciless
3706 silver badges11 bronze badges
For people who wonders why it requires two blank lines
if you were to write in other languages it would be:
fun doSth() {
print()
}
fun doSth1() {
print()
}
but if you were to delete all the curly braces from the code you will see:
two blank lines between methods
fun doSth()
print()
#
#
fun doSth1()
print()
#
answered May 19, 2022 at 12:11
adwardwo1fadwardwo1f
7616 silver badges18 bronze badges
Время на прочтение
7 мин
Количество просмотров 15K
Друзья, у нас для вас отличные новости. Во-первых на улице наконец-то светит солнышко, а это значит, что весна начинает полноправно вступать в свои права. Вторая новость более профильная — уже 20 марта стартует первое занятие в новом потоке по курсу «Разработчик Python», в связи с этим мы публикуем заключительную часть статьи «Знакомство с Тестированием в Python», предыдущие части которой можно прочитать здесь и здесь.
Тестирование в Нескольких Средах
До сих пор вы проводили тесты для одной версии Python с помощью виртуальной среды с определенным набором зависимостей. Но всегда может возникнуть потребность проверить работу приложения на нескольких версиях Python или нескольких версиях пакета. Tox — приложение, автоматизирующее тестирование в нескольких средах.
Установка Tox
Tox доступен на PyPl в виде пакета для установки через pip:
$ pip install tox
После установки, можно переходить к настройке Tox.
Настройка Tox Для Ваших Зависимостей
Tox настраивается через файл конфигурации в каталоге проекта. В нем содержится следующее:
- Команда, которую нужно запустить для выполнения тестов;
- Любые дополнительные пакеты, необходимые для выполнения;
- Целевые версии Python, выбранные для тестирования.
Вместо того, чтобы изучать синтаксис настройки Tox, можно начать с запуска quickstart-приложения.
$ tox-quickstart
Инструмент настройки Tox задаст вам вопросы и создаст файл, похожий на следующий, в tox.ini:
[tox]
envlist = py27, py36
[testenv]
deps =
commands =
python -m unittest discover
Прежде чем запустить Tox, убедитесь, что в папке приложения есть файл setup.py с шагами установки пакета. Если его нет, воспользуйтесь руководством по созданию setup.py.
А если ваш проект не предназначен для распространения на PyPl, можно пропустить это требование, добавив следующую строку в файл tox.ini под заголовком tox:
[tox]
envlist = py27, py36
skipsdist=True
Если вы не создаете setup.py, а в приложении есть некоторые зависимости от PyPl, понадобится уточнить их в разделе testenv. Например, Django потребует следующего:
[testenv]
deps = django
По завершении этого этапа можно запускать тесты.
Теперь можно выполнить Tox, и он создаст две виртуальные среды: одну для Python 2.7 и одну для Python 3.6. Директория Tox называется .tox/. В ней Tox выполнит -m unittest discover для каждой виртуальной среды.
Можно запустить этот процесс, вызвав Tox из командной строки:
$ tox
Tox выдаст результаты тестов для каждого окружения. При первом запуске Tox нужно время на создание виртуальных окружений, но при втором запуске все будет работать гораздо быстрее.
Результаты работы Tox довольно простые. Создаются окружения для каждой версии, устанавливаются зависимости, а затем запускаются тестовые команды.
Есть еще несколько дополнительных параметров командной строки, которые стоит запомнить.
Запуск единственного окружения, например, Python 3.6:
$ tox -e py36
Повторное создание виртуальной среды при изменении зависимости или повреждении side-packages:
$ tox -r
Запуск Tox с менее подробным выводов:
$ tox -q
Запуск Tox с более подробным выводом:
$ tox -v
Более подробно о Tox можно прочитать на сайте документации Tox.
Автоматизация Выполнения Тестов
До сих пор вы выполняли тесты вручную, запуская команду. Но существуют инструменты для автоматического выполнения тестов при внесения изменений и их коммите в репозиторий с системой контроля версий, например, Git. Инструменты для автоматизации тестирования часто называют инструментами CI/CD, что означает “Continuous Integration/Continuous Deployment” (Непрерывная Интеграция/Непрерывное Развертывание). Они могут запускать тесты, компилировать и публиковать приложения, и даже развертывать их на продакшн.
Travis CI — один из многих доступных CI сервисов.
Travis CI хорошо работает с Python, и теперь вы можете автоматизировать выполнение всех созданных тестов в облаке! Travis CI бесплатен для любых проектов с открытым исходным кодом на GitHub и GitLab и доступен за определенную плату для частных проектов.
Для начала, войдите на сайт и выполните аутентификацию с помощью ваших учетных данных GitHub или GitLab. Затем создайте файл с названием .travis.yml со следующим содержанием:
language: python
python:
- "2.7"
- "3.7"
install:
- pip install -r requirements.txt
script:
- python -m unittest discover
Эта конфигурация дает Travis CI следующие указания:
- Тестирование для Python 2.7 и 3.7 (По желанию, можно заменить их на любые другие.)
- Установка всех пакетов, перечисленных в requirements.txt (Можно убрать этот раздел, если у вас нет зависимостей.)
- Запуск python -m unittest discover для запуска тестов.
После коммита и пуша этого файла, Travis CI будет запускать эти команды каждый раз, когда вы пушите в свой удаленный Git-репозиторий. Результаты можно посмотреть на их сайте.
Что Дальше
Теперь вы знаете, как писать тесты, добавлять их в свой проект, выполнять их и даже делать это автоматически, поэтому можно познакомиться с продвинутыми методами, который могут пригодиться по мере роста тестовой библиотеки.
Введение Линтеров в Приложение
У Tox и Travis CI есть настройка тестовой команды. В этом туториале в качестве тестовой команды мы использовали python -m unittest discover.
Вы можете предоставить одну или несколько команд в этих инструментах, что позволит добавлять новые инструменты для улучшения качества приложения.
Одно из таких приложений — линтер. Он будет смотреть ваш код и оставлять комментарии. Тем самым, он может давать советы по поводу ошибок, править конечные пробелы и даже предугадывать потенциальные баги.
Чтобы узнать больше о линтерах, почитайте туториал по Качеству Кода в Python.
Пассивный Линтинг с flake8
flake8 — популярный линтер, который оставляет комментарии о стиле вашего кода в соответствии с PEP 8 спецификацией.
Установить flake8 можно с помощью pip:
$ pip install flake8
Затем можно запустить flake8 для одного файла, папки или шаблона:
$ flake8 test.py
test.py:6:1: E302 expected 2 blank lines, found 1
test.py:23:1: E305 expected 2 blank lines after class or function definition, found 1
test.py:24:20: W292 no newline at end of file
Вы увидите список ошибок и предупреждений в вашем коде, найденных flake8.
flake8 можно настраивать в командной строке или в файле конфигурации проекта. Если вы хотите игнорировать некоторые правила, например E305, показанный выше, то можете задать это в конфигурации. flake8 проверит файл .flake8 в папке проекта или файл setup.cfg. Если вы хотите использовать Tox, то можно добавить раздел настройки flake8 в tox.ini.
В этом примере игнорируются директории .git and __pycache__, а также правило E305. Кроме того, максимальная длина строки увеличивается с 80 знаков до 90. Вы, в какой-то момент, поймете, что стандартное ограничение в 79 символов на строку не подходит для тестов, в которых могут содержаться длинные названия методов, строковые литералы с тестовыми значениями и прочие длинные фрагменты данных. Обычно для тестов увеличивают длину строки до 120 символов:
[flake8]
ignore = E305
exclude = .git,__pycache__
max-line-length = 90
Кроме того, можно предоставить эти параметры в командной строке:
$ flake8 --ignore E305 --exclude .git,__pycache__ --max-line-length=90
Полный список параметров настройки можно посмотреть на Сайте Документации.
Теперь вы можете добавить flake8 к настройке CI. Для Travis CI это будет выглядеть следующим образом:
matrix:
include:
- python: "2.7"
script: "flake8"
Travis прочитает конфигурацию в .flake8 и не сможет завершить сборку при наличии ошибок линтинга. Убедитесь, что вы добавили зависимость flake8 в файл requirements.txt.
Агрессивный Линтинг с Форматтером Кода
flake8 — пассивный линтер, который только рекомендует правки, вносить их в код вам придется самостоятельно. Форматтер кода — более агрессивный подход. Он меняет код автоматически в соответствии со стилями и макетами.
black — очень неумолимый форматтер. В нем нет настроек и он очень дотошный. Что делает его отличным инструментом для вставки в ваш тестовый пайплайн.
Обратите внимание: для black требуется Python версии 3.6 и выше.
Установить black можно с помощью pip:
$ pip install black
Затем, чтобы запустить из командной строки, укажите файл или директорию, которую вы хотите форматировать:
$ black test.py
Следите за Чистотой Тестового Кода
Можно заметить, что при написании тестов копипастить фрагменты кода вы будете гораздо чаще, чем при создании обычных приложений. Время от времени, тесты могут быть очень однообразными, но это совсем не повод бросить код в неаккуратном и разрозненном виде.
Со временем в вашем тестовом коде накопится технический долг, и внести в тесты правки, необходимые при значительных изменениях в коде приложения, окажется очень сложным как раз из-за структуры.
При написании тестов старайтесь следовать принципу DRY: Don’t Repeat Yourself (Не Повторяйтесь).
Тестовые фикстуры и функции — отличный способ писать код, который легко поддерживать. Кроме того, не забывайте про легкость для чтения. Подумайте о развертывании инструментов для линтинга, например, flake8 на ваш тестовый код:
$ flake8 --max-line-length=120 tests/
Тестирование для Выявления Снижения Производительности между Правками
Есть множество способов для бенчмаркинга кода в Python. В стандартной библиотеке есть модуль timeit, который планирует функции несколько раз и показывает вам распределение. В этом примере test() будет выполнен 100 раз, а затем будет дан вывод с помощью print():
def test():
# ... your code
if __name__ == '__main__':
import timeit
print(timeit.timeit("test()", setup="from __main__ import test", number=100))
Если вы решите использовать pytest в качестве исполнителя тестов, обратите внимание на плагин pytest-benchmark. Он предоставляет pytest фикстуру под названием benchmark. Любой вызываемый объект может быть передан benchmark(), он залогирует время вызываемого в результатах pytest.
Установить pytest-benchmark из PyPl можно с помощью pip:
$ pip install pytest-benchmark
Затем можно добавить тест, использующий фикстуру и передающий вызываемый объект на выполнение:
def test_my_function(benchmark):
result = benchmark(test)
Выполнение pytest выдаст вам бенчмарк-результаты:
Узнать больше можно на Сайте Документации.
Тестирование для Выявления Ошибок Безопасности
Еще один тест, который стоит запустить на вашем приложении — проверка на распространенные ошибки и уязвимости безопасности.
Установите bandit из PyPl с помощью pip:
$ pip install bandit
Затем можно передать название вашего модуля приложения с флагом -r и получить краткую информацию:
$ bandit -r my_sum
[main] INFO profile include tests: None
[main] INFO profile exclude tests: None
[main] INFO cli include tests: None
[main] INFO cli exclude tests: None
[main] INFO running on Python 3.5.2
Run started:2018-10-08 00:35:02.669550
Test results:
No issues identified.
Code scanned:
Total lines of code: 5
Total lines skipped (#nosec): 0
Run metrics:
Total issues (by severity):
Undefined: 0.0
Low: 0.0
Medium: 0.0
High: 0.0
Total issues (by confidence):
Undefined: 0.0
Low: 0.0
Medium: 0.0
High: 0.0
Files skipped (0):
Как и в случае с flake8, правила флагов bandit можно настраивать, а если вы хотите игнорировать некоторые из них, можно добавить следующий фрагмент в файл setup.cfg с параметрами:
[bandit]
exclude: /test
tests: B101,B102,B301
Больше информации на сайте GitHub.
Заключение
Python сделал тестирование доступным благодаря встроенным командам и библиотекам, необходимым для проверки корректности работы приложений. Начать тестировать в Python несложно: можно использовать unittest и писать небольшие, легкие в поддержке методы для проверки кода.
По мере того, как вы узнаете больше о тестировании и расширяете ваше приложение, рассмотрите возможность перехода на один из тестовых фреймворков, таких как pytest, чтобы начать использовать более продвинутые функции.
Спасибо, что прочитали. Желаю безошибочного будущего с Python!
А для тех, кто дочитал статью, у нас еще одна отличная новость. Прямо сейчас можно успеть приобрести курс «Разработчик Python» со скидкой 10000 рублей!
Первая часть
Вторая часть
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 |
W = 1 B = 2 def opponent(color): if color == W: return B else: return W def correct_coords(row, col): return 0 <= row < 8 and 0 <= col < 8 class Board: def __init__(self): self.color = W self.field = [] for row in range(8): self.field.append([None] * 8) self.field[0] = [ Rook(W), Knight(W), Bishop(W), Queen(W), King(W), Bishop(W), Knight(W), Rook(W) ] self.field[1] = [ Pawn(W), Pawn(W), Pawn(W), Pawn(W), Pawn(W), Pawn(W), Pawn(W), Pawn(W) ] self.field[6] = [ Pawn(B), Pawn(B), Pawn(B), Pawn(B), Pawn(B), Pawn(B), Pawn(B), Pawn(B) ] self.field[7] = [ Rook(B), Knight(B), Bishop(B), Queen(B), King(B), Bishop(B), Knight(B), Rook(B) ] def current_player_color(self): return self.color def cell(self, row, col): piece = self.field[row][col] if piece is None: return ' ' color = piece.get_color() c = 'w' if color == W else 'b' return c + piece.char() def get_piece(self, row, col): if correct_coords(row, col): return self.field[row][col] else: return None def move_piece(self, row, col, row1, col1): if not correct_coords(row, col) or not correct_coords(row1, col1): return False if row == row1 and col == col1: return False piece = self.field[row][col] if piece is None: return False if piece.get_color() != self.color: return False if not piece.can_move(self, row, col, row1, col1): return False self.field[row][col] = None self.field[row1][col1] = piece piece.set_position(row1, col1) self.color = opponent(self.color) return True def is_under_attack(self, row, col, color): for i in range(8): for j in range(8): if self.field[i][j]: if self.field[i][j].get_color() == color and self.field[i][j].can_move(row, col): return True return False class Piece: def __init__(self, color): self.color = color def can_move(self, board, row, col, row1, col1): if not correct_coords(row, col) or not correct_coords(row1, col1): return False if row == row1 and col == col1: return False if board.cell(row1, col1)[1] != ' ': if board.get_piece(row1, col1).get_color() == self.color: return False return True def set_position(self, row, col): self.row, self.col = row, col def get_color(self): return self.color class Pawn(Piece): def can_move(self, board, row, col, row1, col1): if not super().can_move(board, row, col, row1, col1): return False if col != col1: return False if self.color == W: direction = 1 start_row = 1 else: direction = -1 start_row = 6 if row + direction == row1: return True if (row == start_row and row + 2 * direction == row1 and board.field[row + direction][col] is None): return True return False def char(self): return 'P' class Rook(Piece): def can_move(self, board, row, col, row1, col1): if not super().can_move(board, row, col, row1, col1): return False if row != row1 and col != col1: return False step = 1 if (row1 >= row) else -1 for r in range(row + step, row1, step): if not (board.get_piece(r, col) is None): return False step = 1 if (col1 >= col) else -1 for c in range(col + step, col1, step): if not (board.get_piece(row, c) is None): return False return True def char(self): return 'R' class Knight(Piece): def can_move(self, board, row, col, row1, col1): if not super().can_move(board, row, col, row1, col1): return False if not ((abs(row1 - row) == 2 and abs(col1 - col) == 1) or (abs(row1 - row) == 1 and abs(col1 - col) == 2)): return False return True def char(self): return 'N' class Bishop(Piece): def can_move(self, board, row, col, row1, col1): if not super().can_move(board, row, col, row1, col1): return False if not abs(row1 - row) == abs(col1 - col): return False stepx = 1 if (col1 >= col) else -1 stepy = 1 if (row1 >= row) else -1 for i in range(1, abs(row1 - row)): if not (board.get_piece(row + i * stepy, col + i * stepx) is None): return False return True def char(self): return 'B' class Queen(Piece): def can_move(self, board, row, col, row1, col1): if not super().can_move(board, row, col, row1, col1): return False if abs(row1 - row) == abs(col1 - col): stepx = 1 if (col1 >= col) else -1 stepy = 1 if (row1 >= row) else -1 for i in range(1, abs(row1 - row)): if not (board.get_piece(row + i * stepy, col + i * stepx) is None): return False return True if row == row1 or col == col1: step = 1 if (row1 >= row) else -1 for r in range(row + step, row1, step): if not (board.get_piece(r, col) is None): return False step = 1 if (col1 >= col) else -1 for c in range(col + step, col1, step): if not (board.get_piece(row, c) is None): return False return True return False def char(self): return 'Q' class King(Piece): def can_move(self, board, row, col, row1, col1): if not super().can_move(board, row, col, row1, col1): return False if abs(row - row1) > 1 or abs(col - col1) > 1: return False return True def char(self): return 'K' ( Board, Pawn, Rook, King, Knight, Bishop, Queen, W, B ) board = Board() board.field = [([None] * 8) for i in range(8)] board.field[0][3] = Queen(W) queen = board.get_piece(0, 3) for row in range(7, -1, -1): for col in range(8): if queen.can_move(board, 0, 3, row, col): print('x', end='') else: cell = board.cell(row, col)[1] cell = cell if cell != ' ' else '-' print(cell, end='') print() |






