closures closure中文 - Python lambda绑定到本地值




closure是什么 python自由变量 (3)

以下代码吐出1两次,我希望看到0然后是1

def pv(v) :
  print v


def test() :
  value = []
  value.append(0)
  value.append(1)
  x=[]
  for v in value :
    x.append(lambda : pv(v))
  return x

x = test()
for xx in x:
  xx()

我希望python lambdas绑定到一个局部变量指向的引用,在场景后面。 然而,情况似乎并非如此。 我已经在一个大型系统中遇到了这个问题,其中lambda正在进行现代C ++的等价绑定(例如'boost :: bind'),在这种情况下,你将绑定到智能ptr或复制contstruct lambda的副本。

那么,如何将局部变量绑定到lambda函数并在使用时保留正确的引用? 我对这种行为非常惊讶,因为我不希望这种语言来自垃圾收集器。

有问题的代码如下所示(l3_e是导致问题的变量):

 for category in cat :
      for l2 in cat[category].entries :
        for l3 in cat[category].entries[l2].entry["sub_entries"] :
          l3_e = cat[category].entries[l2].entry["sub_entries"][l3]
          url = "http://forums.heroesofnewerth.com/" + l3_e.entry["url"]
          self.l4_processing_status[l3_e] = 0
          l3_discovery_requests.append( Request(
            url, callback = lambda response : self.parse_l4(response,l3_e)))
          print l3_e.entry["url"]
    return l3_discovery_requests

Answers

lambda的闭包包含对正在使用的变量的引用,而不是其值,因此如果变量的值稍后更改,则闭包中的值也会更改。 也就是说,闭包变量的值在调用函数时解析,而不是在创建函数时解析。 (Python在这里的行为在函数式编程世界中并不罕见,因为它的价值。)

有两种解决方案:

  1. 使用默认参数,在定义时将变量的当前值绑定到本地名称。 lambda v=v: pv(v)

  2. 使用双lambda并立即调用第一个。 (lambda v: lambda: pv(v))(v)


x.append(lambda : pv(v))更改为x.append(lambda v=v: pv(v))

你希望“python lambdas绑定到一个局部变量指向的引用,在场景后面”,但这不是Python的工作方式。 Python在调用函数时查找变量名,而不是在创建函数时查找。 使用默认参数是有效的,因为默认参数在创建函数时计算,而不是在调用函数时计算。

这对lambdas来说并不特别。 考虑:

x = "before foo defined"
def foo():
    print x
x = "after foo was defined"
foo()

版画

after foo was defined

什么是元类? 你用它们做什么的?

TLDR:元类实例化并定义类的行为,就像类实例化一样,并定义实例的行为。

伪代码:

>>> Class(...)
instance

以上应该看起来很熟悉。 那么, Class来自哪里? 它是元类(也是伪代码)的一个实例:

>>> Metaclass(...)
Class

在实际代码中,我们可以传递默认的元类, type ,实例化类所需的一切,我们得到一个类:

>>> type('Foo', (object,), {}) # requires a name, bases, and a namespace
<class '__main__.Foo'>

换句话说

  • 类是一个实例,因为元类是一个类。

    当我们实例化一个对象时,我们得到一个实例:

    >>> object()                          # instantiation of class
    <object object at 0x7f9069b4e0b0>     # instance
    

    同样,当我们使用默认元类, type显式定义一个类时,我们实例化它:

    >>> type('Object', (object,), {})     # instantiation of metaclass
    <class '__main__.Object'>             # instance
    
  • 换句话说,类是元类的实例:

    >>> isinstance(object, type)
    True
    
  • 换句话说,元类是类的类。

    >>> type(object) == type
    True
    >>> object.__class__
    <class 'type'>
    

当您编写类定义并且Python执行它时,它使用元类来实例化类对象(反过来,它将用于实例化该类的实例)。

就像我们可以使用类定义来改变自定义对象实例的行为方式一样,我们可以使用元类定义来改变类对象的行为方式。

它们可以用于什么? 来自docs

元类的潜在用途是无限的。 已探索的一些想法包括日志记录,接口检查,自动委托,自动属性创建,代理,框架和自动资源锁定/同步。

尽管如此,除非绝对必要,否则通常鼓励用户避免使用元类。

每次创建类时都使用元类:

当你编写类定义时,例如,像这样,

class Foo(object): 
    'demo'

您实例化一个类对象。

>>> Foo
<class '__main__.Foo'>
>>> isinstance(Foo, type), isinstance(Foo, object)
(True, True)

它与具有适当参数的函数调用type相同,并将结果分配给该名称的变量:

name = 'Foo'
bases = (object,)
namespace = {'__doc__': 'demo'}
Foo = type(name, bases, namespace)

注意,有些东西会自动添加到__dict__ ,即命名空间:

>>> Foo.__dict__
dict_proxy({'__dict__': <attribute '__dict__' of 'Foo' objects>, 
'__module__': '__main__', '__weakref__': <attribute '__weakref__' 
of 'Foo' objects>, '__doc__': 'demo'})

在两种情况下,我们创建的对象的元类都是type

(关于类__module____module__的内容的__module__是因为类必须知道它们的定义位置,并且__dict____weakref__存在,因为我们没有定义__slots__ - 如果我们定义__slots__我们将节省一些实例中的空格,因为我们可以通过排除它们来禁止__dict____weakref__ 。例如:

>>> Baz = type('Bar', (object,), {'__doc__': 'demo', '__slots__': ()})
>>> Baz.__dict__
mappingproxy({'__doc__': 'demo', '__slots__': (), '__module__': '__main__'})

......但我离题了。)

我们可以像任何其他类定义一样扩展type

这是类的默认__repr__

>>> Foo
<class '__main__.Foo'>

在编写Python对象时,我们默认可以做的最有价值的事情之一就是为它提供一个好的__repr__ 。 当我们调用help(repr)我们知道对__repr__有一个很好的测试,它也需要测试相等性 - obj == eval(repr(obj)) 。 以下对类型类的类实例的__repr____eq__简单实现为我们提供了一个可以改进类的默认__repr__的演示:

class Type(type):
    def __repr__(cls):
        """
        >>> Baz
        Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
        >>> eval(repr(Baz))
        Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
        """
        metaname = type(cls).__name__
        name = cls.__name__
        parents = ', '.join(b.__name__ for b in cls.__bases__)
        if parents:
            parents += ','
        namespace = ', '.join(': '.join(
          (repr(k), repr(v) if not isinstance(v, type) else v.__name__))
               for k, v in cls.__dict__.items())
        return '{0}(\'{1}\', ({2}), {{{3}}})'.format(metaname, name, parents, namespace)
    def __eq__(cls, other):
        """
        >>> Baz == eval(repr(Baz))
        True            
        """
        return (cls.__name__, cls.__bases__, cls.__dict__) == (
                other.__name__, other.__bases__, other.__dict__)

所以现在当我们用这个元类创建一个对象时,在命令行上回显的__repr__提供了比默认值更不丑的视觉:

>>> class Bar(object): pass
>>> Baz = Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
>>> Baz
Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})

通过为类实例定义了一个很好的__repr__ ,我们有更强的调试代码的能力。 但是,使用eval(repr(Class))更多的检查是不太可能的(因为函数不可能从它们的默认__repr__进行评估)。

预期用法: __prepare__命名空间

例如,如果我们想知道创建类的方法的顺序,我们可以提供一个有序的dict作为类的命名空间。 我们将使用__prepare__执行此操作, 如果它在Python 3中实现,则返回该类的命名空间dict

from collections import OrderedDict

class OrderedType(Type):
    @classmethod
    def __prepare__(metacls, name, bases, **kwargs):
        return OrderedDict()
    def __new__(cls, name, bases, namespace, **kwargs):
        result = Type.__new__(cls, name, bases, dict(namespace))
        result.members = tuple(namespace)
        return result

用法:

class OrderedMethodsObject(object, metaclass=OrderedType):
    def method1(self): pass
    def method2(self): pass
    def method3(self): pass
    def method4(self): pass

现在我们记录了这些方法(和其他类属性)的创建顺序:

>>> OrderedMethodsObject.members
('__module__', '__qualname__', 'method1', 'method2', 'method3', 'method4')

注意,这个例子是从docs中改编而来的- 标准库中的新枚举就是这样做的。

所以我们所做的是通过创建一个类来实例化一个元类。我们也可以像对待任何其他类一样对待元类。它有一个方法解析顺序:

>>> inspect.getmro(OrderedType)
(<class '__main__.OrderedType'>, <class '__main__.Type'>, <class 'type'>, <class 'object'>)

并且它大致正确repr(除非我们能找到表示我们函数的方法,否则我们不能再进行评估)。

>>> OrderedMethodsObject
OrderedType('OrderedMethodsObject', (object,), {'method1': <function OrderedMethodsObject.method1 at 0x0000000002DB01E0>, 'members': ('__module__', '__qualname__', 'method1', 'method2', 'method3', 'method4'), 'method3': <function OrderedMet
hodsObject.method3 at 0x0000000002DB02F0>, 'method2': <function OrderedMethodsObject.method2 at 0x0000000002DB0268>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'OrderedMethodsObject' objects>, '__doc__': None, '__d
ict__': <attribute '__dict__' of 'OrderedMethodsObject' objects>, 'method4': <function OrderedMethodsObject.method4 at 0x0000000002DB0378>})




python closures lambda