下面,我想出了人为地将引用变量n2
从f2()
传递到g2(x)
而不是n
中的全局变量f()
并嵌套的方法g()
。在这种情况下,还有其他更好的方法来替换全局变量吗?
from random import randint
# global value var
def f():
global n
n=0
def g():
global n
if randint(1,100)>50: n+=1
for _ in range(100): g()
print(n)
# local reference var
def f2():
n2=[0]
for _ in range(100): g2(n2)
print(n2[0])
def g2(x):
if randint(1,100)>50: x[0]+=1
答案 0 :(得分:2)
简短的答案:您正试图通过引用传递不可变值(整数),并希望对其进行更新。将其包裹在一个很小的类,列表或字典中,就像您正在做的那样。但是,如果您能够略微修改代码,则还有其他方法。
更长的答案 :(注意:这可能不是您问题的直接答案。)
我知道这是人为的例子。但是考虑一下您的实际问题-g2()
是否需要知道有一个变量应该作为其调用的一部分进行更新?有没有办法使变量更新的责任属于定义变量的责任? f2()
是定义变量并更新变量的那个怎么样?这样,您可以将对该变量的所有更改限制在很小的范围内(f2()
)。
在这种情况下,我的处理方式如下:
def f2():
n2 = 0
for _ in range(100):
n2 += g2()
print(n2)
def g2():
return 1 if randint(1,100)>50 else 0
答案 1 :(得分:2)
通过使用函数式语言以及尝试编写可重现的测试,我通常尝试采用一条规则,即函数应将其所有输入声明为参数,并将其所有输出产生为返回值:功能在最大程度上应不会产生副作用。使用global
关键字可能表明您正在违反此规则。
例如,您的“ g”函数采用当前状态变量,然后对其进行递增或不递增。为此,您不需要全局变量(也不需要将其作为嵌套函数):
from random import randint
def g(n):
"""Returns either n or n+1, with 50% probability."""
if randint(1,100)>50:
return n+1
else:
return n
然后,迭代函数可以调用多次:
def f():
"""Produces a number between 0 and 100 with $DISTRIBUTION."""
n = 0
for _ in range(100):
n = g(n)
return n
最后是顶层:
if __name__ == '__main__':
print(f())
由于我们从未完全确定自己的代码,因此可以编写一些测试。
def test_f():
n = f()
assert n >= 0 and n < 100
def test_g():
n = g(0)
assert n == 0 or n == 1
def test_g_dist():
count = 100
ns = [g(0) for _ in range(count)]
assert(all(n == 0 or n == 1) for n in ns)
zeros = len([n for n in ns if n == 0])
ones = len([n for n in ns if n == 1])
assert zeros + ones == count
# won't always pass; probability of failure left as an exercise
assert zeros > 0.45 * count and zeros < 0.55 * count
请注意,我可以根据需要多次拨打f()
和g(n)
,并且它们将永远不会干扰其他任何内容。在启动时运行我自己的单元测试会有点不寻常,但是我可以随意这样做。