我读过你永远不应该测试变量的存在;如果您的程序必须检查变量是否存在,则您不“知道您的变量”,这是一个设计错误。但是,我有一个递归函数,可以在每次调用该函数期间将值添加到字典和列表中。为了避免声明全局变量,我试图使变量成为函数的局部变量。但为了做到这一点,我必须在函数的开头将 myList 和 myDict 声明为 [] 和 {}。当然,这会删除我在之前的递归调用中对 dict 和 list 所做的更改,这是我不想要的。我想过在开始时设置一个 try ... catch ,检查变量是否存在,如果它们还不存在,则只将它们声明为 {} 和 [] ,但我读过这是糟糕的设计。有没有更好的方法来解决这个问题?对于没有附加任何实际代码,我深表歉意,但我仍处于规划此功能的初始阶段,因此没有什么可附加的。
3 回答
创建新的局部变量不会覆盖以前调用的局部变量。每次调用该函数时,都会获得新的局部变量。如果函数以递归方式调用自身,则每次调用都会获得自己的局部变量。从您的解释中很难判断这是否是您问题的答案。你真的需要发布一些代码。
你可以在这里使用一个python “gotcha” 。默认的可变参数(例如字典或列表)将在递归中发生变异:
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 结构。你应该使用它们。
例如,这是一个简单的递归斐波那契函数,它使用记忆化来避免原始版本的 O(2^n) 复杂性(注意,前导下划线仅表示名称是“私有的”,不应该在没有详细知识的情况下使用其内部工作原理):
class _Fib(object):
def __init__(self):
self.cache = {0:0, 1:1}
def __call__(self, n):
if n not in self.cache:
self.cache[n] = self(n - 1) + self(n - 2)
return self.cache[n]
fib = _Fib()
快速测试;请注意,第二次调用返回时没有明显延迟:
>>> 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 ... catch ,检查变量是否存在,如果它们还不存在,则只将它们声明为 {} 和 [] ,但我读过这是糟糕的设计。
这不是糟糕的设计。这是一个坏的设计——它就是行不通。如果您的字典和列表不是全局的,那么您try/except
将始终引发异常,因为在函数的开头,没有定义局部变量。
这让我想到了最后一个想法。也许您想在每次最初调用该函数时创建一个新字典和列表,并在递归停止后将它们丢弃。在这种情况下,必须调整上面的基于类的选项;或者您可以将递归函数包装在一个外部函数中,每次调用它时都会重新创建它们:
def outer(a, b):
return _inner_recursive(a, b, {}, [])
def _inner_recursive(a, b, lst, dct):
#blah blah blah