原文地点: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]
我们能做到更好嘛?
我盘算提一个跟Javascript
的Function.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)