在Python中关闭变量

时间:2019-05-05 00:21:12

标签: python

在方案中我可以说

(define f
  (let ((a (... some long computation ...)))
    (lambda (args)
      (...some expression involving a ...))))

然后,将仅执行一次计算a的长计算,并且a将在lambda内部可用。我甚至可以将set! a设置为其他值。

如何在Python中完成相同的工作?

我查看了许多Google对“ Python闭包”的引用,它们都引用了外部过程中的多个本地过程,这不是我想要的。

编辑:我想编写一个确定数字是否为理想平方的函数。这段代码使用二次残基到各种基数上工作,而且速度很快,平均调用昂贵的平方根函数仅占715(不到1%)的6倍:

def iroot(k, n): # newton
  u, s = n, n+1
  while u < s:
    s = u
    t=(k-1)*s+n//pow(s,k-1)
    u = t // k
  return s

from sets import Set

q64 = Set()
for k in xrange(0,64):
  q64.add(pow(k,2,64))

q63 = Set()
for k in xrange(0,63):
  q63.add(pow(k,2,63))

q65 = Set()
for k in xrange(0,65):
  q65.add(pow(k,2,65))

q11 = Set()
for k in xrange(0,11):
  q11.add(pow(k,2,11))

def isSquare(n):
  if n % 64 not in q64:
    return False
  r = n % 45045
  if r % 63 not in q63:
    return False
  if r % 65 not in q65:
    return False
  if r % 11 not in q11:
    return False
  s = iroot(2, n)
  return s * s == n

我想在isSquare函数中隐藏q64,q63,q65和q11的计算,因此没有其他代码可以修改它们。我该怎么办?

1 个答案:

答案 0 :(得分:3)

典型的Python闭包加上函数是这种语言的一等公民这一事实,几乎就像您要的一样:

def f(arg1, arg2):
  a = tremendously_long_computation()

  def closure():
    return a + arg1 + arg2  # sorry, lack of imaginantion

  return closure

在这里,对f(arg1, arg2)的调用将返回一个函数,该函数在a处关闭并已经计算。唯一的区别是a是只读的,因为闭包是使用静态程序的文本构造的(但是,可以通过使用可变容器的难看的解决方案来避免这种情况)。

对于Python 3,后者似乎可以通过nonlocal关键字来实现。

编辑:出于您的目的,缓存decorator似乎是最佳选择:

import functools


def memoize(f):
  if not hasattr(f, "cache"):
    f.cache = {}

  @functools.wraps(f)
  def caching_function(*args, **kwargs):
    key = (args, tuple(sorted(kwargs.items())))
    if key not in f.cache:
      result = f(*args, **kwargs)
      f.cache[key] = result
    return f.cache[key]

  return caching_function


@memoize
def q(base):
  return set(pow(k, 2, base) for k in xrange(0, base))


def test(n, base):
  return n % base in q(base)


def is_square(n):
  if not test(n, 64):
    return False

  r = n % 45045
  if not all((test(r, 63), test(r, 65), test(r, 11))):
    return False

  s = iroot(2, n)
  return s * s == n

这样,q(base)base才精确计算一次。哦,您也可以将irootis_square设置为可缓存的!

当然,我对缓存装饰器的实现容易出错,并且不会照顾它消耗的内存-最好使用functools.lru_cache(至少在Python 3中) ,但可以很好地了解发生了什么。