doc.dev1x.org

巨大ループ

1. 状況

ループの中で様々な処理が行われており、ループが巨大化している

2. 問題

保守性が低下する

3. 原因

プログラムの構造化不足

4. 対策

filter/map関数を利用する

def main():

    result = []
    list_a = get_list_a()
    list_b = get_list_b()

    for list_a_item in list_a:
        for list_b_item in list_b:
            if list_b_item["name"] == list_a_item["name"]:
                result.append(list_b_item["id"])

    return result
def main():

    result = []
    list_a = get_list_a()
    list_b = get_list_b()

    for list_a_item in list_a:
        _tmp = list(filter(lambda x: x['name'] == list_a_item['name'], list_b))
        result.extend(list(map(lambda x: x["id"], _tmp)))

    return result

ループ処理を関数/メソッドに切り出す

def main():

    result = []
    list_a = get_list_a()
    list_b = get_list_b()

    for list_a_item in list_a:
        tmp1 = filter_by_name(list_a_item['name'], list_b)
        tmp2 = extract_for_id(tmp1)
        result.extend(tmp2)

    return result

def filter_by_name(name, list_):
    return list(filter(lambda x: x['name'] == name, list_))

def extract_for_id(list_):
    return list(map(lambda x: x['id'], list_)

ループ処理自体をクラスに切り出す

class MyDataBuilder:

    def __init__(self, list_a, list_b):
        self.list_a = list_a
        self.list_b = list_b

    def build(self):
        result = []
        for i in self.list_a:
            tmp1 = self._filter_by_name(i['name'], self.list_b)
            tmp2 = self._extract_for_id(tmp1)
            result.extend(tmp2)

        return result

    def _filter_by_name(self, name, list_):
        return list(filter(lambda x: x['name'] == name, list_))

    def _extract_for_id(self, list_):
        return list(map(lambda x: x['id'], list_)


if __name__=='__main__':
    list_a = get_list_a()
    list_b = get_list_b()

    builder = MyDataBuilder(list_a, list_b)
    result = builder.build()

    print(result)

5. 注意事項

処理の目的を明確にすることが重要

6. 関連

参考資料

Appendix-1

事例1

def main():
    result = []
    list_ = [...]

    for item in list_:
        if item['type'] == 'Type01':
            if item['price']  >= 300 and item['price'] <= 500:
                result.append(item)


        if item['type'] == 'Type02':
            if item['price']  >= 400:
                result.append(item)

    return result
def main():
    result = []
    list_ = [...]

    tmp1 = extract_type(list_, 'Type01')
    tmp2 = filter_price(tmp1, 300)
    tmp3 = filter_price(tmp2, 500)
    result.extend(tmp3)

    tmp4 = extract_type(list_, 'Type02')
    tmp5 = filter_price(tmp4, 400)
    result.extend(tmp5)

    return result

def extract_type(list_, type_):
    result = []
    for item in list_:
        if item['type'] == type_:
            result.append(item)

    return result

def filter_price(list_, price):
    result = []
    for item in list_:
        if item['price']  >= price:
            result.append(item)

    return result
class Filter:

    def __init__(self, value=[]):
        self._value = value

    @property
    def value(self):
        return self._value

    def apply(self, function, *args, **kwargs):
        result = function(self.value, *args, **kwargs)
        return Filter(result)

def extract_type(list_, type_):
    result = []
    for item in list_:
        if item['type'] == type_:
            result.append(item)

    return result

def filter_price(list_, price):
    result = []
    for item in list_:
        if item['price']  >= price:
            result.append(item)

    return result

def main():
    result = []
    list_ = [...]

    result.extend(
        Filter(data)
            .apply(extract_type, 'Type01')
            .apply(filter_price, 300)
            .apply(filter_price, 500)
            .value)

    result.extend(
        Filter(data)
            .apply(extract_type, 'Type02')
            .apply(filter_price, 400)
            .value)

    print(result)