カテゴリー

Python基礎

【PEP8&flake8】pythonにおけるPEP8とflake8

2021年4月15日

こんな人にオススメ


pythonについて調べていると「PEP8」っていう規約が出てくるんだけど、あれって何?規約だから守らないといけないの?
あと、守るんならどうすればいい?

ということで、今回はpythonにおけるコーディング規約であるPEP8と、コードの文法チェックツールであるflake8について紹介する。後半では執筆者が個人的によく使うかなと思ったflake8の項目について紹介する。PEP8が決まり事で、それをチェックする道具がflake8という認識。

実はこれらのスタイル・ツールは執筆者が配属されていた研究室では一切気にされておらず、いざ従うと今までのコードを全て改修する必要があった。一方で、大学院を修了して新しいPCとしてM1 Macbook Proを迎え入れたのだが、この時のpython構築でこれらを気にすることで少しでもコーディングをキレイに、良くしようと思った。

研究室のことを少しだけ書いている、執筆者の半生については以下参照。

【M天パ(めがてんぱ)自己紹介】変わって楽しいの繰り返し

自己紹介 急にキツネの戦い ...

続きを見る

これからpythonを始める方、少しかじった方、結構習得したけどこんなこと考えたことなかったという方も、今回の記事をきっかけにコード整形をしてみてはいかがだろうか。

運営者のメガネとです。YouTubeTwitterInstagramも運営中。

自己紹介はこちらから、お問い合わせはこちらからお願いいたします。

運営者メガネ

PEP8

はじめにPEPについて説明する。その後PEP8についての説明をする。

PEP

そもそもPEPとは「Python Enhancement Proposal」の略で、直訳すると「pythonの拡張提案」とかになる。pythonの設計書的なもので、公式サイトがめちゃくちゃ長く最後までスクロールするのがめちゃくちゃ大変。

自分も含めた初心者はPEP全体については深く考えなくていいのかなぁ...

PEP8

深く考えなくてもいいのかなと言った矢先だが、PEP8については簡単で守ったほうが後々が楽。PEP8PEPの8番目で「Style Guide for Python Code」という名称で、「コードは書いている時間よりも読まれている時間の方が長いので、スタイルを合わせて読みやすくしよう」ということ。

flake8

次にflake8について説明する。

flake8とは

flake8とはPEP8でのコードをチェックしてくれるツールのこと。PEP8はただのルールで、実際に自分のコードではルールに則っているのかについては各自で確かめる必要がある。そこでいちいち目で追っていたのでは時間も力も使う。なら機械にさせればいいじゃない。

E1:インデントに関するエラー

ルール自体は「Flake8 Rules」に載っているが、なんせ数が多い。ということで執筆者の独断と偏見で以下のように抽出した。コードはFlake8 Rulesから引用している。なお、はタブを示す。

Indentation contains mixed spaces and tabs (E101)

インデントにタブとスペースが混在してはいけない。

# anti
def get_name(self):
    if self.first_name and self.last_name:
••••→ → return self.first_name + ' ' + self.last_name
    else:
        return self.last_name

# best
def get_name(self):
    if self.first_name and self.last_name:
••••••••return self.first_name + ' ' + self.last_name
    else:
        return self.last_name

Indentation is not a multiple of four (E111)

インデントで使用するスペースの数が4の倍数である方が良い。

# anti
class User(object):
  def __init__(self, name):
    self.name = name

 # best
class User(object):
    def __init__(self, name):
        self.name = name

Expected an indented block (comment) (E115)

インデントされたコードにコメントを書く際に、コメントがインデントされてない。

# anti
def start(self):
    if True:
#       try:
#           self.master.start()
#       except MasterExit:
#           self.shutdown()
#       finally:
#           sys.exit()
        self.master.start()

# best
def start(self):
    if True:
        #  try:
        #      self.master.start()
        #  except MasterExit:
        #      self.shutdown()
        #  finally:
        #      sys.exit()
        self.master.start()

Continuation line missing indentation or outdented (E122)

コード1行を改行する際に、残りのコード(継続行)がインデントされていない、もしくはされすぎている。

# anti
print("Python", (
"Rules"))

# best
print("Python", (
    "Rules"))

Closing bracket does not match indentation of opening bracket's line (E123)

閉じカッコは開きカッコと同じインデントレベルにする。

# anti
result = function_that_takes_arguments(
    'a', 'b', 'c',
    'd', 'e', 'f',
    )

# best
result = function_that_takes_arguments(
    'a', 'b', 'c',
    'd', 'e', 'f',
)

Continuation line with same indent as next logical line (E125)

継続行のインデントレベルが次の行のインデントレベルと同じ時は、継続行のインデントレベルを上げる。

# anti
if user is not None and user.is_admin or \\
    user.name == 'Grant':
    blah = 'yeahnah'

# best
if user is not None and user.is_admin or \\
        user.name == 'Grant':
    blah = 'yeahnah'

E2: スペースに関するエラー

Whitespace after '(' (E201)

開きカッコの後にはスペースを入れない。

# anti
with open( 'file.dat') as f:
    contents = f.read()

# best
with open('file.dat') as f:
    contents = f.read()

Whitespace before ')' (E202)

閉じカッコの前にはスペースを入れない。

# anti
with open('file.dat' ) as f:
    contents = f.read()

# best
with open('file.dat') as f:
    contents = f.read()

Whitespace before ':' (E203)

コロンの前にスペース入れない。

# anti
with open('file.dat') as f :
    contents = f.read()

# best
with open('file.dat') as f:
    contents = f.read()

Whitespace before '(' (E211)

開きカッコの前後にスペースを入れない。

# anti
with open ('file.dat') as f:
    contents = f.read()

# best
with open('file.dat') as f:
    contents = f.read()

Multiple spaces before operator (E221)

演算子の前にスペースは1つだけ。

# anti
num = 10
doubled = num  * 2

# best
num = 10
doubled = num * 2

Multiple spaces after operator (E222)

演算子の後にはスペースは1つだけ。

# anti
num = 10
doubled = num *  2

# best
num = 10
doubled = num * 2

Tab before operator (E223)

演算子の前にはスペースを1つだけ入れる。

# anti
if x→in [1, 2, 3]:
    print(x)

# best
if x in [1, 2, 3]:
    print(x)

Tab after operator (E224)

演算子の後にはスペースを1つだけ入れる。

# anti
if x in→[1, 2, 3]:
    print(x)

# best
if x in [1, 2, 3]:
    print(x)

Missing whitespace around operator (E225)

全ての演算子の前後にスペースが1つ必要。

# anti
if age>15:
    print('Can drive')

#best
if age > 15:
    print('Can drive')

Missing whitespace around arithmetic operator (E226)

算術演算子+, -, /, *の前後にはスペースを1つ入れる。

# anti
age = 10+15

# best
age = 10 + 15

Missing whitespace around modulo operator (E228)

%の前後にはスペースが1つ必要。

# anti
remainder = 10%2

#best
remainder = 10%2

Missing whitespace after ',', ';', or ':' (E231)

, ;, :の後にはスペースを入れる必要がある。

# anti
my_tuple = 1,2,3

# best
my_tuple = 1, 2, 3

Unexpected spaces around keyword / parameter equals (E251)

関数定義時の=の前後にはスペース入れない。

# anti
def func(key1 = 'val1',
         key2 = 'val2'):
    return key1, key2

# best
def func(key1='val1',
         key2='val2'):
    return key1, key2

At least two spaces before inline comment (E261)

インラインコメントの前には少なくとも2つのスペースが必要。

# anti
def print_name(self):
    print(self.name) # This comment needs an extra space

# best
def print_name(self):
    print(self.name)  # Comment is correct now

Inline comment should start with '# ' (E262)

インラインコメント#の後にはスペースが1つ必要。

# anti
def print_name(self):
    print(self.name)  #This comment needs a space

# best
def print_name(self):
    print(self.name)  # Comment is correct now

Block comment should start with '# ' (E265)

ブロックコメントの前には1つのスペースが必要

# anti
#This comment needs a space
def print_name(self):
    print(self.name)

#best
# Comment is correct now
def print_name(self):
    print(self.name)

Too many leading '#' for block comment (E266)

ブロックコメントの先頭の#は1つだけ。

# anti
## Prints hello
print('hello')

#best
# Prints hello
print('hello')

Tab after keyword (E273)

キーワードの後にはスペースが1つ必要。

# anti
def→func():
    pass

# best
def func():
    pass

Tab before keyword (E274)

キーワードの前にはスペースが一つ必要。

# anti
def func():
    if 1→in [1, 2, 3]:
        print('yep!')

# best
def func():
    if 1 in [1, 2, 3]:
        print('yep!')

Missing whitespace after keyword (E275)

キーワードの後に1つのスペースが必要。

# anti
from collections import(namedtuple, defaultdict)

# best
from collections import (namedtuple, defaultdict)

E3: 空白行に関するエラー

Expected 1 blank line, found 0 (E301)

クラスのメソッド間には1行の空白行が必要。

# anti
class MyClass(object):
    def func1():
        pass
    def func2():
        pass

#best
class MyClass(object):
    def func1():
        pass

    def func2():
        pass

Expected 2 blank lines, found 0 (E302)

関数とクラスの間には2行の空白行が必要。

# anti
def func1():
    pass
def func2():
    pass

#best
def func1():
    pass

def func2():
    pass

E4: importに関するエラー

Multiple imports on one line (E401)

1行で複数のimportが行われている。

# anti
import collections, os, sys

#best
import collections
import os
import sys

Module level import not at top of file (E402)

モジュールレベルのimportがファイルの先頭以外で行われている。

# anti
import locale

locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')

import sys

#best
import locale
import sys

locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')

E5: 1行の文字数に関するエラー

Line too long (82 > 79 characters) (E501)

1行の最大文字数は79文字。最大文字数を100もしくは120に変更することは一般的。

E7: ステートメントに関するエラー

Statement ends with a semicolon (E703)

文章の最後にセミコロンはいらない。

# anti
print('Hello world!');

# best
print('Hello world!')

Multiple statements on one line (def) (E704)

関数定義は一行で書く。

# anti
def f(): pass

# best
def f():
    pass

Comparison to true should be 'if cond is true:' or 'if cond:' (E712)

Trueと比較する際に透過演算子==を使用しない。

# anti
x = True
if x == True:
    print('True!')

# best
x = True
if x is True:
    print('True!')

 # or simply:
x = True
if x:
    print('True!')

Do not compare types, use 'isinstance()' (E721)

型の比較にはisinstanceを使用する。

# anti
if type(user) == User:
    print(user.name)

# best
if isinstance(user, User):
    print(user.name)

Do not assign a lambda expression, use a def (E731)

ラムダ式は変数に割り当てない。

# anti
root = lambda folder_name: os.path.join(BASE_DIR, folder_name)

# best
def root(folder_name):
    return os.path.join(BASE_DIR, folder_name)

Do not use variables named 'I', 'O', or 'l' (E741)

l, O, Iという変数名は紛らわしいので使用しない。

#anti
O = 100.0
total = O * 1.08

# best
order = 100.0
total = order * 1.08

Do not define classes named 'I', 'O', or 'l' (E742)

l, O, Iというクラス名は紛らわしいので使用しない。

Do not define functions named 'I', 'O', or 'l' (E743)

l, O, Iという関数名は紛らわしいので使用しない。

W1: インデントに関する警告

Indentation contains tabs (W191)

タブでインデントしない。

# anti
def get_name(self):
    if self.first_name and self.last_name:
    → → return self.first_name + ' ' + self.last_name
    else:
        return self.last_name

#best
def get_name(self):
    if self.first_name and self.last_name:
        return self.first_name + ' ' + self.last_name
    else:
        return self.last_name

W2: スペースに関する警告

Trailing whitespace (W291)

行の最後にスペースがあってはいけない。

# anti
def first_func():
    # The line below has two spaces after its final character
    pass••

No newline at end of file (W292)

ファイルは改行を入れて終わる。

# anti
import os

BASE_DIR = os.path.dirname(os.path.abspath(__file__))

#best
import os

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
# This is a new line that ends the file.

Blank line contains whitespace (W293)

空白行にはタブやスペース入れない。

# anti
def first_func():
    pass
••••  # This line contains four spaces

def second_func():
    pass

# best
def first_func():
    pass

def second_func():
    pass

W3: 空白行に関する警告

Blank line at end of file (W391)

ファイルの最後の改行は1回だけ。

# anti
class MyClass(object):
    pass

# best
class MyClass(object):
    pass

W5: 改行に関する警告

Line break occurred after a binary operator (W504)

二項演算子の前に改行を入れる(W503は逆を言っているが、W503は現在非推奨)。

# anti
income = (gross_wages +
          taxable_interest)

#best
income = (gross_wages
          + taxable_interest)

W6: 廃止された構文について

Invalid escape sequence 'x' (W605)

バックスラッシュ\\だけ使用しない。

# anti
regex = '\\.png$'

# best
regex = r'\\.png$'

pyflakes関連

Module imported but unused (F401)

使わないモジュールはimportしない。

# anti
from collections import namedtuple

my_tuple = ('Grant', 'McConnaughey', 25)

# best
my_tuple = ('Grant', 'McConnaughey', 25)

Import module from line n shadowed by loop variable (F402)

ループ変数にモジュール名を指定しない。

# anti
from os import path

for path in ['file1.py', 'file2.py']:
    print(path)

# best
from os import path

for file_name in ['file1.py', 'file2.py']:
    print(file_name)

'from module import *' used; unable to detect undefined names (F403)

from module import *だとオブジェクトがどこでimportされたのかが分かりにくいので使用しない。モジュール、クラス、関数は明示的に定義する。

# anti
from users.models import *
from auth.models import *

user = User.objects.get(name='Grant')

# best
from users.models import *
from auth.models import *

user = User.objects.get(name='Grant')

Name may be undefined, or defined from star imports: module (F405)

*インポートをしない。代わりに明示的にするか変数を置く。

#anti
from mymodule import *

def print_name():
    print(name)  # name could be defined in mymodule

# best
from mymodule import name

def print_name():
    print(name)

# or define name.
from mymodule import *

def print_name(name):
    print(name)

Redefinition of unused name from line n (F811)

モジュールは1回のみのimport

# anti
import json
import os
import json

# best
import json
import os

List comprehension redefines name from line n (F812)

リスト内包表記での変数名とその他の変数名は別にする。

# anti
i = 1
squares = [i ** 2 for i in range(10)]

# best
i = 1
squares = [num ** 2 for num in range(10)]

Local variable name is assigned to but never used (F841)

関数内のローカル変数が定義したなら使用する。

# anti
def full_name(self):
    # name is defined but not used
    name = self.first_name + ' ' + self.last_name
    return self.first_name

執筆者の実際の設定

最後に実際に執筆者がどのように運用しているのかについて紹介する。

無効にしているものもある

執筆者は普段pythonコードは「Visual Studio Code」というエディタを使用している。このエディタの設定でPEP8の規約違反をチェック、そして自動修正を行うことができるように設定している。

しかし、最初にも書いたがM1 Macbookを迎え入れるまでろくに規約を考えていなかったので、コードを自動修正されると自分が理解できなくなる。したがって、一部の規約に関しては無効にしているものもある。執筆者がVisual Studio CodeのJSON設定で実際に使用している項目を示す。

"python.formatting.autopep8Args": [
    "--ignore=E402, E401, E501, F403, F405, W503",
],
"python.linting.flake8Args": [
    "--ignore=E402, E401, F403, F405, W503",
],

まず、"python.formatting.autopep8Args"は規約違反の部分を自動修正、"python.linting.flake8Args"は規約違反のコード部分を赤の波線で示してくれる。--ignoreの後に上述の記号+番号を入れることで、autopep8での自動修正やflake8の赤波線を消すことができる。

執筆者がautopep8flake8で無効にしている項目とその理由は以下。

  • E402importをファイル先頭以外してる):テンプレートを他のディレクトリから呼ぶ際に無視してしまうから
  • E401importを一行にしない):以前は1行に凝縮していた。解除してもいいと思っている
  • E501(79文字制限):自動修正されると昔のコードがめちゃくちゃになるので検出のみ
  • F403from module import *は使用しない):便利なんだもん...
  • F405*インポートは使用しない):便利なんだもん...
  • W503W504の逆で、演算子の後に改行を入れる):W504と逆の意見だから

Visual Studio CodeにPEP8やflake8を認識させたりするのは他のサイトを自分で調べるか、もしくはいつの日か本ブログでも書くかもしれないのでそれまで待っていただくかしてもらいたい。

ルールを守って楽しくデュ...コーディング

今回はとても長くなってしまい申し訳ない。しかし、本記事で取り上げた内容はまだ一部で、まだまだ多くのルールが存在する。その全てに従うのが理想だろうが本記事で紹介した内容だけでも守るとより読みやすく、統一されたコードになるのではないでしょうか。

最後に参考にさせていただいたサイトについて下記に記します。とても参考になりました。

pycodestyle(pep8) エラーコードチートシート

スイッチボット

2022/11/28

【SwitchBotロックレビュー】これからのスタンダードになりうるスマートロック

こんな人にオススメ SwitchBotからスマートロック「SwitchBotロック」が発売された ...

生活に役立つ

2022/11/28

【メガネ厳選】クソ便利に使っているサービスやアイテム達

このページでは執筆者「メガネ」が実際に使って便利だと感じているサ ...

マウス

2022/9/11

【Logicool MX ERGO vs MX Master 3】ERGOをメインにした決定的な理由

こんな疑問・お悩みを持っている人におすすめ 執筆者はLogicoolのハイエンӠ ...

完全ワイヤレスイヤホン(TWS)

2022/11/21

【ながら聴きイヤホン比較】SONY LinkBuds、ambie、BoCoはどれがおすすめ?

こんな人におすすめ 耳を塞がない開放型のイヤホンに完全ワイヤレスӟ ...

macOSアプリケーション

2022/10/15

【M1 Mac】MacBook Proに入れている便利でニッチなアプリを21個紹介する

こんな人におすすめ MacBookを購入してLINEとか必要最低限のアプリは入れた。 ...

完全ワイヤレスイヤホン(TWS)

2022/10/23

【SENNHEISER MOMENTUM True Wireless 3レビュー】高レベルでバランス型の高音質イヤホン

こんな人におすすめ SENNHEISER MOMENTUM True Wireless 3って実際のところどうなの? 評判は良い ...

完全ワイヤレスイヤホン(TWS)

2022/11/21

【SONY WF-1000XM4レビュー】神とゴミのハーフ&ハーフ

こんな人におすすめ SONYのフラグシップモデル「SONY WF-1000XM4」ってどれくらい性 ...

完全ワイヤレスイヤホン(TWS)

2022/8/19

【Nothing ear (1)レビュー】ライトな完成度、アップデートに期待

こんな人にオススメ 完全ワイヤレスイヤホン(TWS)でスケルトンボディ ...

Pythonを学びたいけど独学できる時間なんてない人へのすゝめ

執筆者は大学の研究室・大学院にて独学でPythonを習得した。

でも社会人になったら独学で行うには時間も体力もなくて大変だ。

時間がない社会人だからこそプロの教えを乞うのが効率的。

ここでは色んなタイプに合ったプログラミングスクールの紹介をする。

  • この記事を書いた人

メガネ

ベンチャー企業のWebエンジニア駆け出し。独学のPythonで天文学系の大学院を修了→新卒を1.5年で辞める→転職→今に至る。
常時金欠のガジェット好きでM1 MacBook Pro x Galaxy S22 Ultraの狂人。
人見知りで根暗だったけど、人生楽しもうと思って良い方向に狂う→人生が楽しい

ガジェットのレビューとPythonコードを記事にしています。ぜひ楽しんでください🦊
自己紹介と半生→変わって楽しいの繰り返し

-Python基礎
-,