通过以下限制访问非本地变量

时间:2013-10-28 10:06:02

标签: python function increment

我试图弄清楚Python练习中的情况 问题是:

定义两个函数:

  • p:打印变量的值
  • q:递增变量
  • 变量的初始值为0

限制:

  • 变量不是位于全局环境中的唯一方式 改变它是通过调用q()
  • 代码引入全局环境的唯一绑定是针对p和q。

以下代码将描述这种情况:

# >>> p()
# 0
# >>> q()
# >>> q()
# >>> p()
# 2
# >>> print([k for k,v in globals().items() if v==2]) 
## checks that a variable with the value ‘2’ does not exist in the global environment.
# [] 

我想得到一些建议,我该如何解决呢 感谢。

6 个答案:

答案 0 :(得分:2)

您可以在函数的本地范围内定义变量:

def p():
    i = [0]
    def p():
        return i[0]
    def q():
        i[0] += 1
    return p, q

p, q = p()

print(p())
# 0
q()
q()
print(p())
# 2
print([k for k,v in globals().items() if v==2]) 
# []

在Python3中,您可以使用nonlocal语句,这样就可以使i成为int而不是列表:

def p():
    i = 0
    def p():
        return i
    def q():
        nonlocal i
        i += 1
    return p, q

答案 1 :(得分:0)

免责声明:这不是一个非常严肃的答案。符合OP中规定的规格!

def p():
    print(p.__dict__.get('smuggled_value',0))

def q():
    p.smuggled_value = getattr(p,'smuggled_value',0) + 1

更严肃的回答:如果您允许pq拥有参数,则此问题很简单。否则你需要做一些漂亮的hacky事情,或者使用全局变量。

修改:按要求说明:

在python中,一切都是对象,包括函数。具有__dict__属性的对象允许使用object.attribute语法进行属性分配。通常,您只对class成员执行此类属性赋值,但函数也允许它。因此,我只允许您创建pq

请注意,您实际上不应该像这样编码。从你的困惑中可以看出,这种编码方式是无法可读。为你的函数提供参数,使它们具有人类可读性。

答案 2 :(得分:0)

这是一个基于闭包的版本,从未向全局范围引入pq以外的任何符号:

@apply
def p():
    x = [0]
    def p():
        print x[0]
    return p

def q():
    p.func_closure[0].cell_contents[0] += 1

肯定更多的是在“hacky”方面,而不是“认真回答”方面,但另一方面,你的要求也是如此。 ; - )


我还要说严格满足......

的要求是不可能的
  

改变它的唯一方法是调用q()

因为在这个例子中你可以手动执行q内部的操作。

(或者你可以利用你的解释器的实现细节,声明一个ctypes指针,甚至在内存级别做更糟糕的黑客......)

答案 3 :(得分:0)

每次调用时,q可以替换p函数:

p = lambda: 0

def q():
    global p
    value = p() + 1
    p = lambda: value

(虽然它并不完全遵循规范 - 实际上没有任何变量增加)

答案 4 :(得分:0)

因为到目前为止还没有人发布过基于类的解决方案:

class _pq(object):
    _x = 0
    @classmethod
    def p(cls):
        print cls._x
    @classmethod
    def q(cls):
        cls._x += 1

p = _pq.p
q = _pq.q
del _pq

答案 5 :(得分:0)

知道了,我的最终答案。只将p和q引入命名空间,永远不会更改它们。该变量位于lambda的默认参数中,该lambda仅调用一次,不带参数,并返回函数。我们避免使用__import __。

将operator放入全局命名空间(对于operator.setitem)
p, q = (lambda i=[0]: 
    (lambda: i[0], 
     lambda: __import__('operator').setitem(i, 0, i[0]+1))
)()

我认为这种解决方案最适合这个问题,例如除了调用q之外,没有办法改变变量,并且它不会在全局命名空间中引入任何不必要的东西。 感觉更像是Javascript而不是Python: - )