Python教程-6. 函数

Functions

Posted by xuepro on November 15, 2017

函数Functions

可以用def关键字定义一个函数,格式为:

def 函数名( 参数 ):
   "函数_文档字符串"
   函数体语句块
   return 表达式]

如下面的 Fibonacci序列计算函数

>>> def fib(n):    # write Fibonacci series up to n
...     """Print a Fibonacci series up to n."""
...     a, b = 0, 1
...     while a < n:
...         print(a, end=' ')
...         a, b = b, a+b
...     print()
...
>>> # 现在我们可以调用刚刚定义的函数fib,传递一个实际参数2000给被调用函数的参数n:
... fib(2000)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597

定义函数在当前的“符号表”中就引入了函数名,你可以将这个函数名赋值给另外一个名字,比如:

>>> fib
<function fib at 10042ed0>
>>> f = fib
>>> f(100)
0 1 1 2 3 5 8 13 21 34 55 89

如果函数体中没有return语句,函数也会返回一个空值“None”:

>>> fib(0)
>>> print(fib(0))
None

当然可以用return返回一个值,如下面程序返回Fibonacci序列的一个列表:

>>> def fib2(n):  # return Fibonacci series up to n
...     """Return a list containing the Fibonacci series up to n."""
...     result = []
...     a, b = 0, 1
...     while a < n:
...         result.append(a)    # see below
...         a, b = b, a+b
...     return result
...
>>> f100 = fib2(100)    # call it
>>> f100                # write the result
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

注:

  • append()是列表对象result的一个方法(所谓的“方法”就是属于一个对象的函数),用于在列表后面添加元素。可以通过”对象A.方法名m”来调用一个”对象A”的”方法名m”。

  • 方法体的开头可以有用三引号”"”括起来的“函数文档串”,用于说明这个函数的目的和作用等。可以用print等内在函数得到这个函数的文档串。例如:

>>> def my_function():
...     """Do nothing, but document it.
...
...     No, really, it doesn't do anything.
...     """
...     pass
...
>>> print(my_function.__doc__)
Do nothing, but document it.

    No, really, it doesn't do anything.

函数的执行会引入一个新的表示函数局部变量的“符号表”,函数内部的变量的赋值将值存储在这个“局部符号表”,当使用(引用)变量时,首先在函数自身的“局部符号表”中查找这个变量( local variables ),然后是它的“包围层符号表”,…,以此类推,直到最外层的“全局符号表”。

因此,不能在函数内部对全局变量赋值,除非在函数内部用关键字“global”说明是一个全局变量(global variables)。如:

 x = 0
y = 0
def incr(x):
    y = x + 1
    return y
incr(5)
print (x, y)        # print是系统定义的函数叫做内在函数

输出结果为:

0 0

如果函数内部要修改全局变量,则需要用关键字global显式说明,如:

def incr2(x):
    global y
    y =  x + 1
    return y
incr2(5)
print x, y

输出结果为:

0 6

默认参数:

函数参数可以才有默认值,调用函数时如果没有提供默认参数对应的实际参数,则该参数就取默认值。

如下定义了一个学生注册的函数叫做enroll,其参数列表中有4个输入参数(后2个是默认参数)。因为大多数学生注册时不需要提供年龄和城市,只提供必须的两个参数。

注意:默认参数一律放在参数列表的最后面。

def enroll(name, gender, age=6, city='Beijing'):
    print 'name:', name
    print 'gender:', gender
    print 'age:', age
    print 'city:', city
enroll('Sarah', 'F')
enroll('Scott', 'F')
enroll('Jone', 'T')
enroll('Bob', 'M', 7)
enroll('Adam', 'M', city='Tianjin')

默认参数的默认值是函数定义时就计算好的,比如

i = 5
def f(arg=i):
    print(arg)

i = 6
f()   # 将输出: 5

注意:默认值只计算一次,当默认参数是可变的比如列表、字典或类实例等可变对象(mutable object)时,需要注意其不同

def f(a, L=[]):
    L.append(a)
    return L

print(f(1))
print(f(2))
print(f(3))

输出为:

[1]
[1, 2]
[1, 2, 3]

如果你不想在连续调用中共享可变的默认值,则默认值可以等于None这个不变对象:

def f(a, L=None):
    if L is None:
        L = []
    L.append(a)
    return L

关键字参数:

调用函数时,可以用关键字参数形式“ kwarg=value”,从而可以不按照函数定义的参数顺序提供参数。

def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
    print("-- This parrot wouldn't", action, end=' ')
    print("if you put", voltage, "volts through it.")
    print("-- Lovely plumage, the", type)
    print("-- It's", state, "!")

函数有一个必须参数(voltage)和3个可选参数(state, action, and type),下面的函数调用都是可以的:

parrot(1000)                                          # 1 个 位置参数
parrot(voltage=1000)                                  # 1 个关键字参数
parrot(voltage=1000000, action='VOOOOOM')             # 2 个关键字参数
parrot(action='VOOOOOM', voltage=1000000)             # 2 个关键字参数
parrot('a million', 'bereft of life', 'jump')         # 3 个位置参数
parrot('a thousand', state='pushing up the daisies')  # 1 个 位置参数, 1 个关键字参数

所谓“位置参数”,就是没有提供关键字,完全按照函数定义中的参数位置来提供的参数。

而下面的是非法:

parrot()                     # 缺少必须参数
parrot(voltage=5.0, 'dead')  # “关键字参数”后不能有“非关键字参数”
parrot(110, voltage=220)     # 同意参数提供了2个值
parrot(actor='John Cleese')  # 未知的关键字参数

“关键字参数”必须在”位置参数”后面。所有“关键字参数”的“关键字”必须匹配函数定义中的参数名字(包括必须参数)。不能给一个参数提供多个值。再如:

>>> def function(a):
...     pass
...
>>> function(0, a=0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: function() got multiple values for keyword argument 'a'

可变参数和字典参数

 参数名前有2个星号的参数 “ **name ” 表示可以接受一个字典参数。

 参数名前有1个星号的参数 “ *name ” 表示可以接受一个tuple参数。

 注意:*name 参数必须出现在 **name 参数前.

如可变参数:

>>> def concat(*args, sep="/"):
...     return sep.join(args)
...
>>> concat("earth", "mars", "venus")
'earth/mars/venus'
>>> concat("earth", "mars", "venus", sep=".")
'earth.mars.venus'

可变参数在字典参数前:

def cheeseshop(kind, *arguments, **keywords):
    print("-- Do you have any", kind, "?")
    print("-- I'm sorry, we're all out of", kind)
    for arg in arguments:
        print(arg)
    print("-" * 40)
    for kw in keywords:
        print(kw, ":", keywords[kw])

调用:

 cheeseshop("Limburger", "It's very runny, sir.",
           "It's really very, VERY runny, sir.",
           shopkeeper="Michael Palin",
           client="John Cleese",
           sketch="Cheese Shop Sketch")

结果:

-- Do you have any Limburger ?
-- I'm sorry, we're all out of Limburger
It's very runny, sir.
It's really very, VERY runny, sir.
----------------------------------------
shopkeeper : Michael Palin
client : John Cleese
sketch : Cheese Shop Sketch

解封参数列表

当参数在一个tuple或list中,而函数需要传递的是位置参数时,此时需要将tuple 或 list中的参数分离出来,可以使用1个星号 *分别对tuple或list中的参数进行分离。 例如range函数需要单独的start和end参数,,而参数在一个列表中:

>>> args = [3, 6]
>>> list(range(*args))            # call with arguments unpacked from a list
[3, 4, 5]

假如参数在一个字典中,则要用2个星号 **将它们分离出来,如:

>>> def parrot(voltage, state='a stiff', action='voom'):
...     print("-- This parrot wouldn't", action, end=' ')
...     print("if you put", voltage, "volts through it.", end=' ')
...     print("E's", state, "!")
...
>>> d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
>>> parrot(**d)
-- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !

Lambda Expressions (Lambda表达式)

对于小的匿名函数,可以写成Lambda Expressions形式,比如定义一个带2个参数的如下Lambda Expressions:

lambda a, b: a+b

lambda表达式的格式

 func = lambda 参数: 调用参数的表达式

用匿名函数有个好处,因为函数没有名字,不必担心函数名冲突。此外,匿名函数也是一个函数对象,和普通函数对象一样使用,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数。

比如,要定义一个函数,返回两个参数x+y的值,用def可以写:

def add(x, y):
    return x + y
print add(1,2)

用lambda表达式,我们可以这么写:

add=lambda x,y : x + y    ## 将lambda表达式匿名函数)赋给变量add。
print add(1,2)            ## 通过add调用lambda表达式(匿名函数)

如同嵌套函数,Lambda Expressions可以引用包围它的符号表的变量:

>>> def make_incrementor(n):
...     return lambda x: x + n
...
>>> f = make_incrementor(42)
>>> f(0)
42
>>> f(1)
43

上面的Lambda Expressions引用了包围它的函数”make_incrementor” 的变量(参数)n.

现在有一个list=[2, 4, 6, 7, 8],想对里面每个元素进行平方,生成一个新的newlist。我们通常可以这样写:

list = [1,3,4,6,7,9]
newlist = [ ]
for i in list:
    newlist.append(i*i)
print(newlist) #输出: [1, 9, 16, 36, 49, 81]

我们可以用Map函数,传入一个lambda表达式(匿名函数),这么写:

sqr = lambda a: a*a
print ( map(sqr, [1,3,4,6,7,9]) )  #输出: [1, 9, 16, 36, 49, 81]

Map函数的使用格式如下: newlist = Map(func, list),将list中每个元素都进行一个func函数的计算,生成一个新的list 这种一个函数可以接受另外一个函数作为参数的编程方式叫作“函数式编程”。

我们也可以传入一个def定义的普通函数作为map的第一个参数。代码如下:

def square(x):
    return x*x
print( map(sqr, [1,3,4,6,7,9]) )   #输出: [1, 9, 16, 36, 49, 81]

再看一个接受函数作为参数的reduce函数,其格式如下:

      result = reduce(func, 容器对象)

result = reduce(func, [a1, a2, a3…]) 对列表list [a1, a2, a3…]中元素,左到右进行func计算,先计算func(a1, a2), 在计算func(func(a1, a2), a3)…。

# reduce(f, [x1, x2, x3, x4]),其效果就是:f(f(f(x1, x2), x3), x4)
from functools import reduce
def add(x, y):
    return x + y
print ( reduce(add, [47,11,42,13]) )   #输出:113

# We can also use lambda expression as the first parameter
add_ = lambda a,b: a+b
print ( reduce(add_,[47,11,42,13]) )   #输出:113

# 
print(  reduce(lambda a,b: a+b,[47,11,42,13]) )  #输出:113

计算过程,如图所示:

递归函数:调用自身的函数。

和其他编程语言一样。

def fact(n):
    if n==1:  ## 如果n等于1,就直接返回值1
        return 1
    return n * fact(n - 1)
fact(5)             # 输出: 120

fact(5)的计算过程

===> fact(5)
===> 5 * fact(4)
===> 5 * (4 * fact(3))
===> 5 * (4 * (3 * fact(2)))
===> 5 * (4 * (3 * (2 * fact(1))))
===> 5 * (4 * (3 * (2 * 1)))
===> 5 * (4 * (3 * 2))
===> 5 * (4 * 6)
===> 5 * 24
===> 120

快速排序,原理可以网上搜索

def quicksort(arr):
    if len(arr) <= 1:                          ## 如果 输入序列长度小于等于1,直接返回
        return arr
    pivot = arr[len(arr) // 2]                  ##任意选择一个如中间元素作为基准元素,将原序列一分为二
    left = [x for x in arr if x < pivot]        ## left是小于基准元素组成的子序列
    middle = [x for x in arr if x == pivot]     ## middle是等于基准元素组成的子序列
    right = [x for x in arr if x > pivot]       ##  right是大于基准元素组成的子序列
    return quicksort(left) + middle + quicksort(right)  ##对left、right重复上述过程

print(quicksort([3,6,8,10,1,2,1]))
# Prints "[1, 1, 2, 3, 6, 8, 10]"

Refer to:

https://docs.python.org/3/tutorial/controlflow.html


支付宝打赏 微信打赏

您的打赏是对我最大的鼓励!