原文地址:http://alon.horev.net/blog/2013/10/20/argument-binding-in-python/ 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
不是function[译注:我以为这样的术语仍是直接用英文比较准确],而且常常得不到一个function应该获得的结果。partial
用纯python很容易实现,但我只能猜测,考虑到性能,它是用C来实现的。来看一些例子: app
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__
方法。 python2.7
如下表示了方法调用是怎么工做的: 函数
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>>
>>> 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.
>>> 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)
你能够实现一个你本身的返回一个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: .net
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)
就像个人朋友@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)