python(18): 函数基本知识(第12章)
函数几乎是每一种编程语言都会有的一种结构,python也不例外。然而,python中的函数也有几点与一般的函数概念不一样。在python中,“一切皆对象”,函数在python中也是一种对象。因此,一个函数对象可以使用赋值语句赋给一个变量,也可以作为一个成员存贮在一个list中。函数分为两种,一种是普通函数,一种是匿名函数。使用函数的最基本需求是为了重用代码。来看看它们的形式:
def (arg1, arg2...):
...
- lambda x: x*2
第一种通过def定义的函数就是普通的函数,它有一个函数名,。第二种通过lambda定义的函数就是一个匿名函数。
在python中,def语句与其他的语句,比如:print,都是可以执行的。def语句的作用是把函数体的内容打包用于创建一个函数对象,然后把这个函数对象赋给函数名。这里有一个隐式的赋值的过程。def语句可以出现在一个模块(一个.py文件)的任何地方,比如出现在if语句中。同时,由于函数也是一个对象,因此下面的几种操作也是完全可以的:
- if x ==0:
- def npower(n): x**2
- else:
- def npower(n): x**3
- nload = npower
- nload(5)
- L = [npower, nload]
重点是第4行,可以用=像其他的赋值语句一样,把一个函数对象赋给一个变量nload,这样nload也就指向了同一个函数对象;也可以使用小括号里加上参数的方式来调用(第6行);还可以把函数对象作为一个list的成员(第7行)。
总之,函数也是一种对象,def的作用是创建一个函数对象,并把这个函数对象赋给一个标识符,这一标识符就成为了函数名。lambda的作用只是创建一个函数对象,但并没有一个赋值过程。函数的调用方式是函数名加上一对小括号,小括号中是传递给函数的参数。
在python中,参数的传递是通过赋值来传递的。也就是说像上面的例子的第6行,有一个n=5这样一个隐性的赋值过程。这时有一个问题需要注意,比如:
- pp = lambda x: x.append(5)
- L = range(3)
- pp(L)
- def pw(x): x+=1
- one = 1
- pt(one)
这三行的代码的第1行用lambda创建一个函数对象,并把它赋给了pp这个标识符。因此,这一行的作用就相当于:def pp(x): x.append(5),这个函数的作用是给传进来的参数添加一个成员,5。第2行生成了一个list,[0,1,2]。第3行调用了pp这个函数。问题是现在L的值是什么呢?是[0,1,2,5]。由于在python中参数是通赋值传递的,因此第3行就相当于有x = L这么一行赋值语句,也就是说x与L指向了相同的list对象,在函数内部x被增加了一个成员。因此,L的内容也就变了。比较第4行到第6行就能更明白所谓“参数传递是一个赋值过程”的说法。在第4行中创建了一个函数pt,作用是把参数x加上1。经过第6行调用pw函数一次后,one的值会不会变呢?答案是不会变,仍为1。来看看详细的过程。在调用pt函数时,有一个x=one的赋值过程,此时x和one指向了同一个整数对象。函数内部,x+=1,相当于 x = x+1,而由于x是一个整数,是不可变的(介绍基本类型的时候介绍过),x+1会创建一个新的整数对象,并把这个对象赋给了x。也就是说在函数内部其实并没有改变x原来指向的内容,而只是让x指向了一个新的对象。总结起来说就是,如果传递给函数的参数是“可变的”,像list和dictionary,那么在函数内部就有可能改变这个参数的值。
对于函数还有一个"本地变量"的概念,也就是说在一个函数内部定义的标识符,只在这个函数被调用时存在。如果一个函数要对它所处模块的变量进行赋值操作,那么就需要用到global语句,比如:global S,这条语句的意思是从模块中引入变量S。(下一篇会详细介绍这方面的内容)
从上面提到的几个例子中也可以看到,在定义函数时并没有在任何地方标明参数是什么类型,返回的是什么类型。这也表明,可以把任何类型的对象传给函数,函数也可以返回任何对象,一个函数可以在不同的条件下返回不同类型的值,这也是python灵活性的表现之一。比如:
- def calBook(n):
- if n%2 == 0:
- return n**2
- else:
- return False这个函数可能返回整数对象,也可能返回False这个boolean对象。此外,虽然对n的类型没有限定,但是也很容易知道,n必须支持%和**操作符。如果我们自己定义的对象也支持这两种操作符,那么也就可以作为这个函数的参数。为了说明这个问题,再来看一个函数:
- def haveStar(f):
- return f * 2就这么一个简单的函数,一个对象只要支持*这种操作,就可以作为参数传给haveStar这个函数。比如:数值型的 1 2.3 5.6 5+6.5j等等;string类的,'have' 'two'等。这个函数根据传入参数的类型的不同进行不同的操作,如果传入的是数值型,则进行算术的乘法运算,如果传入的是字符串,那就是把字符串复制两遍并返回。这就是python中,函数的多态性的体现。其实函数对于一个对象的界面(即它具有的属性和方法,或者说能进行什么操作)比对它的类型更为关心。比如说*这个操作符,它不关心两边就是什么类型的东西,只要这两个东西都支持*操作就可以了。这里说的界面的概念,估计以后还会用到的。
再重复一句,在python中,一切皆对象。