清理嵌套函数调用

时间:2014-01-08 19:03:32

标签: python function nested

我已经编写了几个按顺序运行的函数,每个函数都将前一个函数的输出作为输入,所以为了运行它,我必须运行这行代码

make_list(cleanup(get_text(get_page(URL))))

而我发现丑陋且效率低下,是否有更好的方法进行顺序函数调用?

4 个答案:

答案 0 :(得分:5)

实际上,这与您想要重构常用的复杂表达式或语句的任何情况相同:只需将表达式或语句转换为函数即可。你的表达式恰好是函数调用的组合这一事实没有任何区别(但见下文)。

所以,显而易见的事情就是编写一个包装函数,将函数组合在一起,所以在其他任何地方你都可以对包装器进行简单的调用:

def get_page_list(url):
    return make_list(cleanup(get_text(get_page(url))))

things = get_page_list(url)
stuff = get_page_list(another_url)
spam = get_page_list(eggs)

如果你不总是调用完全相同的函数链,你总是可以将你经常调用的部分分解出来。例如:

def get_clean_text(page):
    return cleanup(get_text(page))
def get_clean_page(url):
    return get_clean_text(get_page(url))

这种重构也为使代码更加冗长但调试更容易打开了大门,因为它只出现一次而不是多次:

def get_page_list(url):
    page = get_page(url)
    text = get_text(page)
    cleantext = cleanup(text)
    return make_list(cleantext)

如果你发现自己需要经常对组合函数进行这种重构,你总是可以编写一个生成重构函数的帮助器。例如:

def compose1(*funcs):
    @wraps(funcs[0])
    def composed(arg):
        for func in reversed(funcs):
            arg = func(arg)
        return arg
    return composed

get_page_list = compose1(make_list, cleanup, get_text, get_page)

如果你想要一个更复杂的compose函数(例如,允许传递多个args /返回值),设计可能会有点复杂,所以你可能想要查看PyPI和ActiveState对于各种现有的实现。

答案 1 :(得分:2)

你可以尝试这样的事情。我总是喜欢分离火车残骸(书中的“清洁代码”称那些嵌套的功能列车残骸)。这更容易阅读和调试。请记住,阅读代码的时间可能比编写代码长两倍,因此更容易阅读。你以后会感谢自己。

url = get_page(URL)
url_text = get_text(url)
make_list(cleanup(url_text))

# you can also encapsulate that into its own function
def build_page_list_from_url(url):
    url = get_page(URL)
    url_text = get_text(url)
    return make_list(cleanup(url_text))

答案 2 :(得分:0)

选项:

  1. 重构:将这一系列函数调用实现为一个恰当命名的方法。
  2. 了解装饰者。它们是以这种方式“链接”功能的语法糖。例如。实施cleanupmake_list作为装饰器,然后用它们装饰get_text
  3. 撰写功能。请参阅this answer中的代码。

答案 3 :(得分:0)

您可以使用以下内容缩短类似的结构:

class ChainCalls(object):
    def __init__(self, *funcs):
        self.funcs = funcs
    def __call__(self, *args, **kwargs):
        result = self.funcs[-1](*args, **kwargs)
        for func in self.funcs[-2::-1]:
            result = func(result)
        return result

def make_list(arg): return 'make_list(%s)' % arg
def cleanup(arg):   return 'cleanup(%s)' % arg
def get_text(arg):  return 'get_text(%s)' % arg
def get_page(arg):  return 'get_page(%r)' % arg

mychain = ChainCalls(make_list, cleanup, get_text, get_page)

print( mychain('http://is.gd') )

输出:

make_list(cleanup(get_text(get_page('http://is.gd'))))