[译]python中的变量绑定

原文地点:http://alon.horev.net/blog/2013/10/20/argument-binding-in-python/

首发:http://www.everlose.info/notes/2013/10/26/Python%E4%B8%AD%E7%9A%84%E5%8F%98%E9%87%8F%E7%BB%91%E5%AE%9A/

在近来一次关于pythono中的变量绑定的争辩以后,我决议从正反两方面列出一些在差别要领中python的变量绑定状况。我们先从可行的要领最先吧。

def add(x,y):
    return x + y

from functools import partial
add5_partial = partial(add, 5)
add5_partial(10)   # 15  

add5_lambda = lambda x: add(x, 5)
add5_lambda(10)    # 15  

我对partial的埋怨

partial不是function[译注:我以为如许的术语照样直接用英文比较正确],而且常常得不到一个function应当获得的效果。partial用纯python很轻易完成,但我只能猜测,考虑到机能,它是用C来完成的。来看一些例子:

1.Partial在methods里不能事情:

from functools import partial  

class Cell(object):
    def set_state(self, state):
        self._state = state  
    set_alive = partial(set_state, state=True)  
    set_dead = partial(set_state, state=False)

>>>Cell().set_alive()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: set_state() takes exactly 2 arguments (1 given)  

这是为何?

你晓得self总是在挪用实例要领的时刻被赋值为该实例吗?这是运用descriptors机制来是完成的。为了完成这个功用,function范例须要完成__get__要领。

以下示意了要领挪用是怎样事情的:

class Person(object):
    def __init__(self, name):
        self._name = name
    def speak(self):  
        print 'My name is ', self._name  

>>> p = Person('Neo')  
>>> p.speak()   # 要领挪用  
My name is Neo  

>>> # 那末函数(function)和要领(method)的区分是什么呢?  
>>> method = p.speak()  # 这个method封装了实例和函数  
>>> method  
<bound method Person.speak of <__main__.Person object at 0x109d7bb90>>  
>>> method.im_self    # 这是self隐蔽的处所  
<__main__.Person object at 0x109d7bb90>>  
>>> method.im_func    # 这是function隐蔽的处所  
<function Person.speak at 0x106163950>  
>>> method()          # 与method.im_func(method.im_self)效果雷同  
My name is Neo  

>>> # 从function到method传递了些什么?  
>>> Person().speak()    # 触发 __getattribute__('speak')  
>>> # __getattribute__从实例的__dict__中搜刮属性  
>>> # 然后__getattribute__从类的__dict__中搜刮属性  
>>> # 当它找到以后,它会搜检这个值(function)是否是完成了一个__get__要领  
>>> # 假如没有完成__get__,返回这个值  
>>> # 假如完成了__get__,返回__get__所返回的值,不论是什么  
>>> method = Person.speak.__get__(Person('Neo'))  
>>> method  
>>> <bound method ?.speak of <__main__.Person object at 0x7f8527ccbad0>>  

2.partial不能搜检:

>>> import inspect, functools  
>>> p = functools.partial(lambda x, y: x + y, 10)  
>>> inspect.getargspect(p)  
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'getargspect'  

>>> print p.__doc__    # 没有坚持被包裹的function的__doc__  
partial(func, *args, **keywords) - new function with partial application
    of the given arguments and keywords.  

3.partial能够更平安,考证变量的数目和称号:

>>> from functools import partial  
>>> f = partial(lambda: None, 1, 2, 3)   # 为何在这里不搜检信号?!  
>>> f()  
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: <lambda>() takes no arguments (3 given)  

partial的替代品

你能够完成一个你本身的返回一个function的partial:

from functools import wraps  

def partial(func, *a, **k):  
    @wraps(func)
    def new_func(*args, **kwargs):  
        return func(*(a + args), **dict(k, **kwargs))
    return new_func  

备注:不要运用上面的代码,它没有保证键的变量唯一。

你也能够运用lambda:

class Cell(object):  
   def set_state(self, state):
       self._state = state  
   set_alive = lambda self: self.set_state(True)
   set_dead = lambda self: self.set_state(False)  

关于lambdas,我的题目

就像我的朋侪@EyalIl说的:

Lambdas猎取变量,partial猎取值。
后者平常更有效。

这里有一个例子能够说清这个题目:

callbacks = []  
for i in xrange(5):
    callbacks.append(lambda: i)  
>>> print [callback() for callback in callbacks]
[4, 4, 4, 4, 4]  

为何发生了这个?

由于python支撑闭包(一个一般很好的东西):

var = 1
f = lambda: var
print f()  # 1
var = 2  
print f()  # 2
# 然则,然则python是怎样晓得的?好吧,function能够hold住外部变量的一个援用  
print f.func_closure() #  (<cell at 0x101bdfb40: int object at 0x7fd3e9c106d8>,)  注:我不晓得这作者是怎样得出来的,我的测试未得出如许的效果,而是抛出TypeError: 'NoneType' object is not callable的毛病  

# 这些cells是什么?cell是一个指向某个外部局限某个称号的一个指针。它hold住了一个许可转变的反射,以至是转变不可变的数据范例。  
print f.func_closure()[0].cell_contents  # 2  

[注:上面这段代码我在python2.7.3和python3.3.1下测试都没获得作者所说的效果,假如有懂的望见教.]

将不是函数(function)参数的变量绑定为函数(function)变量是一个解决要领:

callbacks = []  
for i in xrange(5):
    callbacks.append(lambda x=i:x)
>>> print [callback() for callback in callbacks]  
[0, 1, 2, 3, 4]  

我们能做到更好嘛?

我盘算提一个跟JavascriptFunction.bind功用类似的一个机制。

这是我想它所起的作用(这只是一个发起,这些代码不能真正的事情):

def add(x, y):  
    return x + y

from functools import partial
add5_partial = partial(add, 5)     # 须要一次import
add5_lambda = lambda x: add(x, 5)  # 太长了  

add5_bind = add.bind(5)  # 最短的  

import inspect
>>> print inspect.getargspec(add)  
ArgSpec(args=['x', 'y'], varargs=None, keywords=None, defaults=None)  
>>> print inspect.getargspect(add5_bind)  # works with inspect
ArgSpec(args=['y'], varargs=None, keywords=None, defaults=None)  
    原文作者:木头lbj
    原文地址: https://segmentfault.com/a/1190000000328166
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞