重构代码/合并函数(例如嵌套的for循环顺序)

时间:2012-06-03 16:36:10

标签: python for-loop refactoring dry

只是一点背景:我正在创建一个程序,用户输入一个骨架文本,两个数字(下限和上限)以及一个单词列表。输出是对骨架文本的一系列修改。

示例输入:

text = "Player # likes @." (replace # with inputted integers and @ with words in list)
lower = 1
upper = 3
list = "apples, bananas, oranges"

用户可以选择首先迭代数字:

Player 1 likes apples.
Player 2 likes apples.
Player 3 likes apples.

或首先说的话:

Player 1 likes apples.
Player 1 likes bananas.
Player 1 likes oranges.

我选择通过基于数字键(用户输入的整数)或单词键(来自输入列表中的单词)创建不同类型的字典来拆分这两种输出方法,然后再迭代这些值中的值字典。

以下是两种类型的字典创建:

def numkey(dict): # {1: ['Player 1 likes apples', 'Player 1 likes...' ] }

    text, lower, upper, list = input_sort(dict)
    d = {}

    for num in range(lower,upper+1):
        l = []
        for i in list:
            l.append(text.replace('#', str(num)).replace('@', i))
        d[num] = l
    return d

def wordkey(dict): # {'apples': ['Player 1 likes apples', 'Player 2 likes apples'..] }

    text, lower, upper, list = input_sort(dict)
    d = {}

    for i in list:
        l = []
        for num in range(lower,upper+1):
            l.append(text.replace('#', str(num)).replace('@', i))
        d[i] = l
    return d

我有两个单独的函数来创建不同类型的字典,但我发现两者之间有很多重复。有没有什么方法可以创建一个字典函数并传入不同的值,这将改变嵌套for循环的顺序,以创建我正在寻找的特定{key:value}对?

我不确定如何做到这一点。是否有任何与函数式编程或其他范例相关的东西可能对此有所帮助?问题是有点抽象,比任何东西更具风格/设计导向。

3 个答案:

答案 0 :(得分:2)

您不需要字典来生成输出。你可以使用类似的东西:

import itertools

numbers = range(lower, upper + 1)
words = "a, b, c".split(", ")

data = (numbers, words) if numbers_first else (words, numbers)
for n, w in itertools.product(*data):
    if not numbers_first: n, w = w, n
    print("Player %d likes %s." % (n, w))

为避免循环中的if,您可以动态生成格式字符串,例如:

template = "Player # likes @."
subs = ("{n}", "{w}") if numbers_first else ("{w}", "{n}")
format = make_format(template, subs) # escape {}, replace # and @

# ...
for n, w in product(*data):
    print(format.format(n=n, w=w))

答案 1 :(得分:1)

我认为在这种情况下,不要强迫两个进程合并为一个函数,只需使每个函数更短:

def numkey(dict):
    text, lower, upper, list = input_sort(dict)
    d = {x: [text.replace('#',str(x)).replace('@',item) 
            for item in list.split(', ')] for x in xrange(lower,upper+1)}
    return d

def wordkey(dict):
    text, lower, upper, list = input_sort(dict)
    d = {item: [text.replace('#',str(x)).replace('@',item) 
            for x in xrange(lower,upper+1)] for item in list.split(', ')}
    return d

你可以在理论上使用make_dict(dict, outer, inner)或类似的东西重构dict创建,但我认为最终对于如此短的代码片段不太清楚,特别是考虑到你必须预处理以某种方式进行投注。

答案 2 :(得分:1)

这是一种更通用的方式:

from itertools import product

def generate(skeleton, replacements):
    values = (product([k], v) for k, v in replacements.items())
    for p in product(*values):
        s = skeleton
        for a, b in p:
            s = s.replace(a, str(b))
        yield s

replacements应该是字典{placeholder: list of values},例如:

gen = generate("Player # likes @.", {'#': range(1,3), '@': ['apples', 'bananas']})    
for phrase in gen:
    print phrase

打印

Player 1 likes apples.
Player 2 likes apples.
Player 1 likes bananas.
Player 2 likes bananas.

如果您需要其中一个值为“static”,只需提供单个元素列表:

gen = generate("Player # likes @.", {'#': [1], '@': ['apples', 'bananas']})    

请注意,这适用于任意数量的占位符:

gen = generate("Player # likes @ and can $", {
    '#': range(1,3), 
    '@': ['apples', 'bananas'],
    '$': ['swim', 'run', 'jump']
})