上集对比学习了js
和python
中的数据类型。发现在数据类型这方面,python
比js
略胜一筹,各种方便。
今天再来对比学习一下函数。
####函数的定义
在不同的语言中,定义函数的语法不同这个很正常,更何况python
是一个摒弃了大括号的语言,所以定义函数跟js
完全不同,这里不做展开说明了。在这里主要着重说一说在参数和函数调用等方面,js
和python
的异同。
####函数的参数
在js
中,函数的参数分为传值和传址。展开来说就是基本类型(string,number,bool等)会采用传值的方式,但是高等类型(object,array,function)等会采用传址的方式,这两张方式的异同在这里就不做展开了,只要明白有这两张方式就好,举个简单传址的例子:
var arg = {"name":"a"}
function change(obj) {
obj["name"] = "b";
return obj;
}
console.log(change(arg)); // {"name":"b"}
console.log(arg); // {"name":"b"}
这里乱入一下php
,在php
中,传给函数的参数,传值还是传址是可以在定义函数时,进行定义的,例如:
# 传值
$arg = array(
"name"=>"a"
);
function change($arg) {
$arg["name"] = "b";
return $arg;
};
print_r(change($arg)); # ([name]=>b)
print_r($arg); # ([name]=>a)
# 传址
$arg = array(
"name"=>"a"
);
function change(&$arg) {
$arg["name"] = "b";
return $arg;
};
print_r(change($arg)); // ([name]=>b)
print_r($arg); // ([name]=>b)
但是在python
中,你需要在定义函数和调用函数时,确定是要传值还是传址,例如:
# 传址
arg = {"name":"a"}
def change(arg):
arg["name"] = "b"
return arg
print change(arg) # {"name":"b"}
print arg # {"name":"b"}
# 传值
arg = {"name":"a"}
def change(**arg):
arg["name"] = "b"
return arg
print change(**arg) # {"name":"b"}
print arg # {"name":"b"}
看代码可以看到,使用**
这个符号,可以将dict
这种类型改为传值的方式。在python
中,如果一个函数的参数前面加了**
,就证明这个函数接收的将是一个key
个数不确定的dict
,在调用参数时,需要传入name="a"
这种类型的数据(也就是字符串),才会将其转换成dict
。
另一方面,在调用函数时,给参数加上**
,会使其声明为name="a"
这样的类型,在函数定义参数同样有**
时,会变成传值的方式。
####高阶函数
所谓高阶函数,就说指可以将函数本身作为参数的函数。
在js
中,函数的参数本身就可以是任何类型的数据,所以js
本身就带有高阶函数的特征。
在python
中,是通过map
和reduce
这两种方法来实现高阶函数的。
先来说说map
,map
接收两个参数,第一个是调用的函数,第二个是一个序列,如下:
def foo(x):
return x * x
map(foo,[1,2,3,4,5,6]); # [1,4,9,16,25,36]
再来说说reduce
,reduce
的用法比map
复杂一些,reduce
同样接收两个参数,第一个是要调用的函数,第二个是一个序列。但是不同的是,reduce
参数中的函数,也必须接收两个参数,第一个参数是序列中第n-1个值的计算结果,第二个是序列中第n个值,如下所示:
def foo(x,y):
return x + y
reduce(foo,[1,2,3]) # foo(foo(1,2),3); # 6
####高阶函数拓展
上面已经说了,在js
中,函数天生具有高阶函数的特征,在js
中,很多对象的方法本身就是高阶函数,例如sort
方法:
var arr = [5,4,3,2,1];
var arr2 = arr.sort(function (a,b) {
return a - b;
});
在python
中,排序的方法也很类似:
def foo (a,b):
return a - b
arr = [5,4,3,2,1]
arr2 = sorted(foo,arr);
除了参数中可以传入函数之外,高阶函数还可以在返回值中返回参数。这个在js
中我们通常叫做闭包,在python
中,你也可以这么称呼。
####匿名函数
在js
中,匿名函数是很常见的使用方式:
var foo = function (a) {
return a * a;
};
在python
中,匿名函数的使用不如在js
中方便,使用方法如下:
map(lambda a : a * a,range(3));
python
的匿名函数只允许有一个表达式,该表达式的结果就是函数的返回值,所以这就限定了匿名函数不可以太复杂,但是这样也有好处,就是可以在需要的地方更加简洁的进行函数计算,例如上述的一行代码,就可以求出[1,2,3]
的平方数组,只用一行代码!
####装饰器
装饰器这个概念在js
中是不存在的,可以看做是python
中一种非常独特的语法糖。
装饰器本身的作用是将某一函数当做参数,在另一函数中调用,从而起到增强或者装饰该函数的作用。
装饰器的用法如下:
@decorator
def foo(a):
return a + 1
装饰器本身也是一个高阶函数,需要自己定义,例如:
def decorator(fun):
@functools.wraps(fun)
def wrapper(*args,**kw):
return fun(*args,**kw)
return wrapper
使用装饰器,相当于层层调用装饰器的返回值,直到没有return
(最后一个return应该是调用被装饰的函数)。
在使用装饰器时,还有一点需要注意,就是要将装饰函数的__name__
设置为被装饰函数的__name__
,这样才会像直接调用被装饰函数一样。