2

我正在尝试找出 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.
# [] 

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

4

6 回答 6

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
于 2013-10-28T10:16:57.343 回答
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.

请注意,您实际上不应该像这样编写代码。从您的困惑中可以看出,这种编码方式绝不是可读的。为您的函数提供参数,以便它们是人类可读的。

于 2013-10-28T10:45:23.267 回答
0

这是一个基于闭包的版本,除了全局范围之外p,它从不引入任何符号:q

@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指针,甚至在内存级别上做更糟糕的黑客攻击......)

于 2013-10-28T10:54:32.763 回答
0

也许 q 可以在每次调用时替换 p 函数:

p = lambda: 0

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

(虽然它并不完全符合规范——实际上没有任何变量被递增)

于 2013-10-28T11:02:17.537 回答
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
于 2013-10-28T11:04:47.867 回答
0

明白了,我的最终答案。仅将 p 和 q 引入命名空间,从不更改它们。该变量位于 lambda 的默认参数中,该参数仅被调用一次,没有参数,并返回函数。我们避免使用 __import__ 将运算符放入全局命名空间(对于 operator.setitem)。

p, q = (lambda i=[0]: 
    (lambda: i[0], 
     lambda: __import__('operator').setitem(i, 0, i[0]+1))
)()

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

于 2013-10-28T11:28:55.097 回答