Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/286.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 在递归函数的开头声明空变量_Python - Fatal编程技术网

Python 在递归函数的开头声明空变量

Python 在递归函数的开头声明空变量,python,Python,我读过,你永远不应该测试变量的存在;如果您的程序必须检查变量是否存在,则您不“知道您的变量”,这是一个设计错误。但是,我有一个递归函数,在每次调用该函数时向字典和列表添加值。为了避免声明全局变量,我试图使变量成为函数的局部变量。但是为了做到这一点,我必须在函数的开头将myList和myDict声明为[]和{}。当然,这会删除我在以前的递归调用中对dict和list所做的更改,这是我不想要的。我想试试。。。在开始时捕获,检查变量是否存在,如果它们还不存在,则只将它们声明为{}和[],但我读到这是一

我读过,你永远不应该测试变量的存在;如果您的程序必须检查变量是否存在,则您不“知道您的变量”,这是一个设计错误。但是,我有一个递归函数,在每次调用该函数时向字典和列表添加值。为了避免声明全局变量,我试图使变量成为函数的局部变量。但是为了做到这一点,我必须在函数的开头将myList和myDict声明为[]和{}。当然,这会删除我在以前的递归调用中对dict和list所做的更改,这是我不想要的。我想试试。。。在开始时捕获,检查变量是否存在,如果它们还不存在,则只将它们声明为{}和[],但我读到这是一个糟糕的设计。有没有更好的办法?我很抱歉没有附加任何实际的代码,但我仍处于规划此函数的开始阶段,因此没有什么需要附加的

创建新的局部变量不会覆盖以前调用的局部变量。每次调用函数时,都会得到新的局部变量。如果函数递归地调用自身,则每个调用都将获得自己的局部变量。从你的解释很难判断这是否是你问题的答案。你真的需要发布一些代码。

这里有一个你可以利用的工具。默认的可变参数(如字典或列表)将在递归中发生变化:

def my_function(d={}):
    ...
    #recursion will mutate d
小心使用,但它可能有用

一个简单的例子:

def f(a,d=[]):
    d.append(a)
    if a!=2:
        f(2)
    return d

print f(1) # [1,2]

如果您确实需要访问递归函数中的可变状态,您可能应该使用一个类

在这种情况下,使用不支持面向对象编程(OOP)的语言时,最好的方法通常只是使用全局变量,可能包含在它们自己的模块中,以避免名称空间冲突。全局变量是“坏的”,但有时它们是两个(或更多)邪恶中的较小者。但是您正在用Python编写代码,它为您提供了一组丰富的OOP构造。你应该使用它们

例如,这里有一个简单的递归Fibonacci函数,它使用memonization来避免原始版本的O(2^n)复杂性(请注意,前导下划线仅表示名称为“private”,在没有详细了解其内部工作原理的情况下不应使用):

快速测试;请注意,第二次呼叫返回时没有明显延迟:

>>> map(fib, xrange(10))
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
>>> fib(249)
4880197746793002076754294951020699004973287771475874L
您可以看到,这个“函数”(实际上是一个可调用对象)通过在创建可变对象时创建它来解决您描述的问题。然后它通过
self
访问可变对象。有比上面更好的方法来编写
fib
,但是对于需要状态的更复杂的递归函数来说,这可能是一个很好的基本设计

当然你还有很多其他选择。使用另一个答案中提到的可变默认值非常类似——但我更喜欢使用类,因为它更显式。您甚至可以将对象存储为函数的属性

def foo(a, b):
    if base_case(a, b):
        return
    foo.dct[a] = b
    foo.lst.append((a, b))
    foo(a - 1, b - 1)
foo.dct = {}
foo.lst = []
我不喜欢这个建筑,但它值得了解。最后,不要完全忽视最明显的解决方案:

def foo(a, b, dct, lst):
    if base_case(a, b):
        return
    dct[a] = b
    lst.append((a, b))
    foo(a - 1, b - 1, dct, lst)
有时候这其实是最好的方式!这当然是简单明了的

最后,我要指出,您似乎误解了功能范围的性质。你说

我想试试。。。在开始时捕获,检查变量是否存在,如果它们还不存在,则只将它们声明为{}和[],但我读到这是一个糟糕的设计

这个设计不错。这是一个破设计,根本不起作用。如果您的字典和列表不是全局的,那么您的
try/except
将始终引发异常,因为在函数的开头,没有定义任何局部变量

这让我想到了最后一点。也许您希望在每次最初调用函数时创建一个新的字典和列表,并在递归停止后丢弃它们。在这种情况下,必须调整上面基于类的选项;或者,您可以将递归函数包装在外部函数中,以便在每次调用时重新创建它们:

def outer(a, b):
    return _inner_recursive(a, b, {}, [])

def _inner_recursive(a, b, lst, dct):
    #blah blah blah

“我读过,你永远不应该测试变量的存在……”永远不要说“永远”。另外,如何显示您的代码?我的直觉是,可能有更好的方法来做到这一点。
def outer(a, b):
    return _inner_recursive(a, b, {}, [])

def _inner_recursive(a, b, lst, dct):
    #blah blah blah