Js与Python函数赋值与绑定
这几天Gogo在玩Python。
作为咱学的第二门脚本语言,Gogo自然会将它与JavaScript作对比。
函数式编程
函数式编程主要有这么几个特征:
- 函数可以相互赋值
- 函数可以嵌套定义
- 函数可以作为函数的参数、返回值
实际上与面向对象十分相似:
- 对象可以相互赋值
- 类可以嵌套定义
- 对象可以作为函数的参数、返回值
Js和Py都支持函数式编程,也支持面向对象,但它们在函数赋值的地方却有些差异。
函数赋值
Js在函数赋值的地方有个小坑,先来看一段代码。
var value = 5;
var obj = {value: 10};
obj.fun = function(){
console.log(this.value);
}
var f = obj.fun;
obj.fun(); // 10
f(); // 5
为什么函数赋值以后结果会发生变化?
其实与this的变化有关。obj.fun这个方法中,this指向的是obj对象,value结果便是10。
而当obj.fun赋值给 f 时,this的值就发生了改变,指向的全局对象(浏览器上就相当于window对象)。
f() 实际上就相当于 window.f()。
想要解决这个问题也十分简单:
f = obj.fun.bind(obj);
f(); //10
bind 这个方法能绑定 this,上面这段代码也就指定了this指向obj,也就解决了赋值后this变化的问题。
再来看看Python,相对于Js函数赋值的诟病,Python则完全没有这样的困扰,函数赋值后self不会改变。
class A:
def __init__(self, value):
self.value = value
def fun(self):
print(self.value)
obj = A(10)
f = obj.fun
obj.fun() # 10
f() # 10
骤然看上去,似乎Python函数赋值机制更为完善,但其实也有它的问题。
动态添加方法
在Js里,动态给一个对象添加方法是十分简单的。
var obj = {value: 10};
obj.fun = function(){
console.log(this.value);
}
直接给属性赋值一个函数就好。
在Python中,直接赋值可能会出现问题。原因就在于Python函数中的self是不会改变的,因此必须要手动绑定self到对象。
class A:
def __init__(self, value):
self.value = value
def fun(self):
print(self.value)
obj = A(10)
obj.fun = fun # 这是错误的做法
obj.fun() # TypeError: fun() missing 1 required positional argument: 'self'
正确的做法应该这样
def bind(self, function):
def call(*arg, **argv):
return function(self, *arg, **argv)
self.__setattr__(function.__name__, call)
bind(obj, fun)
obj.fun() # 10
bind这个方法用于绑定一个函数到某个对象。
原理十分简单,调用obj.fun()实际调用的是call函数,call函数把self和参数传递到绑定的fun中,就实现了self的绑定。
小结
Python和JavaScript在函数赋值上的差异,似乎体现了它们不同的设计风格。
Js显得更加自由,Py则显得更加严谨。
当然这只是Gogo初窥两门语言后的想法,更多的有趣之处,得等以后再去挖掘了。
本文根据 CC BY-NC-SA 4.0 License 协议授权
本文链接:https://blog.gogo.moe/20170131_Js%E4%B8%8EPython%E5%87%BD%E6%95%B0%E8%B5%8B%E5%80%BC%E4%B8%8E%E7%BB%91%E5%AE%9A/