🔨

Pythonで理解するコーディングアンチパターン

2021/07/09に公開

条件分岐編

if文のネスト

bad

age = int(input())
if age >= 0:
    if age < 20:
        print("You're a minor")

good

age = int(input())
if 0 <= age < 20:
    print("You're a minor")

複雑な条件式に説明変数や関数抽出を行わない

bad

threshold = 60
score_limit = 100
math_score = int(input())
english_score = int(input())
science_score = int(input())
if threshold <= math_score <= score_limit and threshold <= english_score <= score_limit and threshold <= science_score <= score_limit:
    print("You have passed all the subjects")
else:
    print("You have not passed all the subjects")

good

def is_all_subjects_passed(*args):
    threshold = 60
    score_limit = 100

    for score in args:
        if not threshold <= score <= score_limit:
            return False
    return True


math_score = int(input())
english_score = int(input())
science_score = int(input())
if is_all_subjects_passed(math_score, english_score, science_score):
    print("You have passed all the subjects")
else:
    print("You have not passed all the subjects")

if-else文の否定文を先に記述

bad

savings = int(input())
if not savings > 0:
    print("You have no savings")
else:
    print("Your savings is {} yen".format(savings))

good

savings = int(input())
if savings > 0:
    print("Your savings is {} yen".format(savings))
else:
    print("You have no savings")

変数編

マジックナンバーを使う

bad

# ['6', '7', '8']だけでは、何を示しているのか分からない
if input() in ['6', '7', '8']:
    print("It's summer now")
else:
    print("It's not summer now")

good

month = input()
summer_seasons = ['6', '7', '8']
if month in summer_seasons:
    print("It's summer now")
else:
    print("It's not summer now")

定義した場所と使用する場所が離れている

bad

age = 15
# height,genderは二つ目以降のifで使われるため、ここで宣言する必要がない
height = 175
gender = 'man' 

# 定数であるAVERAGE_HEIGHT_OF_MANについても、ここで宣言する必要がない
ADULT_AGE = 20
AVERAGE_HEIGHT_OF_MAN = 170

if 0 <= age <= ADULT_AGE:
    print("You're a minor")
else:
    print("You're not a minor")

if height >= AVERAGE_HEIGHT_OF_MAN:
    print("You're taller than average height")
else:
    print("You're shorter than average height")

if gender == "man":
    print("You are a man")
elif gender == "woman":
    print("You are a woman")
else:
    print("Gender isn't just male and female, is it?")

good

age = 15
ADULT_AGE = 20
if 0 <= age <= ADULT_AGE:
    print("You're a minor")
else:
    print("You're not a minor")

height = 175
AVERAGE_HEIGHT_OF_MAN = 170
if height >= AVERAGE_HEIGHT_OF_MAN:
    print("You're taller than average height")
else:
    print("You're shorter than average height")

gender = 'man'
if gender == "man":
    print("You are a man")
elif gender == "woman":
    print("You are a woman")
else:
    print("Gender isn't just male and female, is it?")

特定の関数でしか使わない変数をグローバルで定義

bad

class Person:

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def introduce_yourself(self):
        print("=========================")
        print("My name is {}".format(self.name))
        print("I'm {} years old now".format(self.age))
        print("=========================")


person = Person("kite", 15)
person.introduce_yourself()

# personクラスの変数を書き換えられてしまう。
person.name = "michel"
person.age = 35
person.introduce_yourself()
=========================
My name is kite
I'm 15 years old now
=========================
=========================
My name is michel
I'm 35 years old now
=========================

good

class Person:

    def __init__(self, name, age):
        self.__name = name
        self.__age = age

    def introduce_yourself(self):
        print("=========================")
        print("My name is {}".format(self.__name))
        print("I'm {} years old now".format(self.__age))
        print("=========================")


person = Person("kite", 15)
person.introduce_yourself()
person.__name = "michel"
person.introduce_yourself()

=========================
My name is kite
I'm 15 years old now
=========================
=========================
My name is kite
I'm 15 years old now
=========================

発音しにくい命名

bad

# Dictionary for managing personal informationの略であるが、読みにくく分かりにくい
dfmpi = {"name":"taylor", "age":20}
print("My name is {}".format(dfmpi["name"]))
print("I'm {} years old".format(dfmpi["age"]))

good

# Dictionary for managing personal information
personal_info = {"name":"taylor", "age":20}
print("My name is {}".format(personal_info["name"]))
print("I'm {} years old".format(personal_info["age"]))

検索しにくい命名

bad

import datetime

dt = datetime.datetime.now()
# 検索欄でvariableの候補が最低でも3つ出ることとなり、煩雑となる
variable_for_current_second = dt.second
variable_for_current_minute = dt.minute
variable_for_current_hour = dt.hour
print("{}時{}分{}秒".format(variable_for_current_hour, variable_for_current_minute,
                        variable_for_current_second))

good

import datetime

dt = datetime.datetime.now()
sec = dt.second
min = dt.minute
hour = dt.hour
print("{}時{}分{}秒".format(hour, min, sec))

関数編

同じ処理を関数にせず複数箇所に記述

bad

spring_season = [3, 4, 5]
summer_season = [6, 7, 8]
fall_season = [9, 10, 11]
winter_season = [12, 1, 2]
season = None
month = int(input())

if month in spring_season:
    season = "spring"
elif month in summer_season:
    season = "summer"
elif month in fall_season:
    season = "fall"
elif month in winter_season:
    season = "winter"
print("Current season is {}".format(season))

next_month = int(input())
if next_month in spring_season:
    season = "spring"
elif next_month in summer_season:
    season = "summer"
elif next_month in fall_season:
    season = "fall"
elif next_month in winter_season:
    season = "winter"
print("Next month's season is {}".format(season))

good

def get_current_season(month):
    spring_season = [3, 4, 5]
    summer_season = [6, 7, 8]
    fall_season = [9, 10, 11]
    winter_season = [12, 1, 2]
    season = None

    if month in spring_season:
        season = "spring"
    elif month in summer_season:
        season = "summer"
    elif month in fall_season:
        season = "fall"
    elif month in winter_season:
        season = "winter"
    return season


month = int(input())
print("Current season is {}".format(get_current_season(month)))
print("Next month's season is {}".format(get_current_season(month + 1)))

関数名から何をするか分からない

bad

class Dog:

    def __init__(self, name, age):
        self.__name = name
        self.__age = age

    # get,setだけでは、何に対してgetし、何に対しsetするか分からない
    def get(self):
        return self.__name

    def set(self, name):
        self.__name = name

good

class Dog:

    def __init__(self, name, age):
        self.__name = name
        self.__age = age

    def get_name(self):
        return self.__name

    def set_name(self, name):
        self.__name = name

引数にbooleanを渡す

bad

def calculate_annual_income( is_there_bonus, saraly, monthly_benefit):
    all_months = 12
    total_saraly = salary * all_months
    yearly_benefit = monthly_benefit * all_months
    annual_income = (total_saraly + yearly_benefit) * 0.8
    
    # この時点で年収計算しているのに、booleanがあることでボーナスがあるか否かの判定もしなければならない。
    if is_there_bonus:
        bonus_magification = 2.5
        annual_income += salary * bonus_magification
    return annual_income

good

def calculate_annual_income(bonus, saraly, monthly_benefit):
    all_months = 12
    total_saraly = salary * all_months
    yearly_benefit = monthly_benefit * all_months
    annual_income = (total_saraly + yearly_benefit) * 0.8 + bonus
    return annual_income

関数が複数のことをしている

bad

import math

# ざっくりと面積を計算するメソッドのため、多くのことを許容してしまっている
def calculate_area(bottom, upper_bottom, height, radius):
    assert bottom * upper_bottom * height * radius > 0
    triangle_area = bottom * height / 2
    trapezoid = (bottom + upper_bottom) * height / 2
    circle = radius**2 * math.pi
    return triangle_area, quadrilateral, trapezoid, circle

good

import math


def calculate_traiangle_area(bottom, height):
    return bottom * height / 2


def calculate_trapezoid_area(bottom, upper_bottom, height):
    return (bottom + upper_bottom) * height / 2


def calculate_circle(radius):
    return radius**2 * math.pi

Discussion