③[函数]

一、函数

定义: 函数是指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名即可

特性:

1.代码重用

2.保持一致性

3.可扩展性

1.函数的创建

格式

def 函数名(参数列表):

函数体

 

函数的参数

1.必须的参数

必需参数须以正确的顺序传入函数。调用时的数量必须和声明时的一样。

def f(name,age):

 

    print('I am %s,I am %d'%(name,age))

f('xuanyi',23)

 

2.关键字参数

关键字参数和函数调用关系紧密,函数调用使用关键字参数来确定传入的参数值。使用关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值。

f(age=16,name='alvin')

3.缺省参数(默认参数)

调用函数时,缺省参数的值如果没有传入,则被认为是默认值。下例会打印默认的sex,如果sex没有被传入:

def print_info(name,age,sex='male'):

 

4.不定长参数

你可能需要一个函数能处理比当初声明时更多的参数。这些参数叫做不定长参数,和上述2种参数不同,声明时不会命名。

def add(*tuples):

    sum=0

    for v in tuples:

        sum+=v

 

    return sum

 

print(add(1,4,6,9))

加了星号(*)的变量名会存放所有未命名的变量参数[放在左边]。而加(**)的变量名会存放命名的变量参数[放在右边]

def print_info(name,*args,**kwargs):#def print_info(name,**kwargs,*args):报错

 

    print('Name:%s'%name)

 

    print('args:',args)

    print('kwargs:',kwargs)

 

    return

 

print_info('alex',18,hobby='girl',nationality='Chinese',ability='Python')

# print_info(hobby='girl','alex',18,nationality='Chinese',ability='Python')  #报错

#print_info('alex',hobby='girl',18,nationality='Chinese',ability='Python')   #报错

注意,还可以这样传参:

ef f(*args):

    print(args)

 

f(*[1,2,5])

 

def f(**kargs):

    print(kargs)

 

f(**{'name':'alex'})

 

 

补充(高阶函数):

高阶函数是至少满足下列一个条件的函数:

·接受一个或多个函数作为输入

·输出一个函数

def add(x,y,f):

    return f(x) + f(y)

 

res = add(3,-6,abs)

print(res)

 

 

函数的返回值

return

作用:

1.结束函数

2.返回某个对象

注意点:1.如果函数没有return会默认返回一个None

2.如果return多个对象,那么python会把多个对象封装成一个元组返回

 

函数的作用域

1. python中的作用域分4种情况:

L:local,局部作用域,即函数中定义的变量;

E:enclosing,嵌套的父级函数的局部作用域,即包含此函数的上级函数的局部作用域,但不是全局的;

G:globa,全局变量,就是模块级别定义的变量;

B:built-in,系统固定模块里面的变量,比如int, bytearray等。 搜索变量的优先级顺序依次是:作用域局部>外层作用域>当前模块中的全局>python内置作用域,也就是LEGB。

当然,local和enclosing是相对的,enclosing变量相对上层来说也是local。

2.作用域的产生

在Python中,只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块(如if、try、for等)是不会引入新的作用域的,如下代码:

3.global关键字

当内部作用域想修改外部作用域的变量时,就要用到global和nonlocal关键字了,当修改的变量是在全局作用域(global作用域)上的,就要使用global先声明一下,代码如下:

4. nonlocal关键字 

global关键字声明的变量必须在全局作用域上,不能嵌套作用域上,当要修改嵌套作用域(enclosing作用域,外层非全局作用域)中的变量怎么办呢,这时就需要nonlocal关键字了

注:当执行一条nonlocal语句时,nonlocal名称必须已经在一个嵌套的def作用域中赋值过

nonlocal限制作用域查找仅为嵌套的def,nonlocal不会再嵌套的模块的全局作用域或所有def之外的内置作用域中查找,即便已经有了这些作用域

 

5.python3.0 Keyword-Only参数

注:keyword-only参数必须在一个单个星号后面指定,而不是两个星号----命名的参数不能出现在**args任意关键字形式的后面,并且一个**不能独自出现在参数列表中。

6.python3.0中的函数注解

python为声明注解提供了特殊的语法,但是,它自身不做任何事情;注解完全是可选的,并且,出现的时候只是直接附加到函数对象的_annotations_属性以供其他用户使用

注解只在def语句中有效,在lambda表达式中无效,因为lambda的语法已经限制了它所定义的函数工具。

 

小结 

(1)变量查找顺序:LEGB,作用域局部>外层作用域>当前模块中的全局>python内置作用域;

(2)只有模块、类、及函数才能引入新作用域;

(3)对于一个变量,内部作用域先声明就会覆盖外部变量,不声明直接使用,就会使用外部作用域的变量;

(4)内部作用域要修改外部作用域变量的值时,全局变量要使用global关键字,嵌套作用域变量要使用nonlocal关键字。nonlocal是python3新增的关键字,有了这个 关键字,就能完美的实现闭包了。 

 

二、高阶函数

def f(n):
   
return n*n

print(f(4))

def foo(a,b,func):
    func(a)+func(b)
    ret=func(a)+func(b)
   
return ret

foo(
1,2,f)
print(foo(1,2,f))

 

1.函数名可以进行赋值

2.函数名可以作为函数参数,还可以作为函数的返回值

def foo3():

    def inner():

        return 8

    return inner



print(foo3())

#返回的是函数inner对象的内存地址

 

三、递归函数

定义:在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。

#1.调用自身函数

#2.有一个结束条件

但凡

例:阶乘

def factorial(n):

 

    result=n

    for i in range(1,n):

        result*=i

 

    return result

 

print(factorial(4))

 

 

#**********递归*********

def factorial_new(n):

 

    if n==1:

        return 1

    return n*factorial_new(n-1)

 

print(factorial_new(3))

 

递归特性:

1. 必须有一个明确的结束条件

2. 每次进入更深一层递归时,问题规模相比上次递归都应有所减少

3. 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返     回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。)

 

四、内置函数(Python3)

重要的内置函数:


1.filter(function, sequence)

str = ['a', 'b','c', 'd']

 

def fun1(s):

    if s != 'a':

        return s

 

 

ret = filter(fun1, str)

 

print(list(ret))# ret是一个迭代器对象

 

对sequence中的item依次执行function(item),将执行结果为True的item做成一个filter object的迭代器返回。可以看作是过滤函数。

由于range和filter都返回可迭代对象,在python3.0中,它们需要list调用来显示其所有结果

 

  2 .map(function, sequence) 

传入一个用户定义的函数来对它们进行充分的利用,从而可以对列表中的每一个元素应用这个函数:map对每个列表中的元素都调用了inc函数,并将所有的返回值收集到一个新的列表中。

map函数是用来进行函数式编程的这类工具中最简单的内置函数代表:函数式编程的意思就是对序列应用一些函数的工具。例如:基于某一测试函数过滤出一些元素(filter),以及对每对元素都应用函数并运行到最后结果(reduce).

str = [1, 2,'a', 'b']

 

def fun2(s):

 

    return s + "alvin"

 

ret = map(fun2, str)

 

print(ret)      #  map object的迭代器

print(list(ret))#  ['aalvin', 'balvin', 'calvin', 'dalvin']

对sequence中的item依次执行function(item),将执行结果组成一个map object迭代器返回.
map也支持多个sequence,这就要求function也支持相应数量的参数输入:

def add(x,y):

    return x+y

print (list(map(add, range(10), range(10))))

##[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

 

  3. reduce(function, sequence, starting_value)  

from functools import reduce

 

def add1(x,y):

    return x + y

 

print (reduce(add1, range(1, 101)))## 4950 (注:1+2+...+99)

 

print (reduce(add1, range(1, 101), 20))## 4970 (注:1+2+...+99+20)

对sequence中的item顺序迭代调用function,如果有starting_value,还可以作为初始值调用.

 

4. lambda

就像def一样,这个表达式创建了一个之后能够调用的函数,但是它返回了一个函数而不是将这个函数赋值给一个变量名。这也就是lambda有时叫做匿名(也就是没有函数名)的函数的原因。实际上,它们常常以一种行内进行函数定义的形式使用,或者用作推迟一些执行代码。

lambda是一个表达式。能够出现在python语法不允许def出现的地方--例如:在一个列表常量中或者函数调用的参数中。此外,作为一个表达式,lambda返回了一个值(一个新的函数),可以选择性地赋值给一个变量名。相反,def语句总是得在头部将一个新的函数赋值给一个变量名,而不是将这个函数作为结果返回。

       普通函数与匿名函数的对比:

#普通函数

def add(a,b):

    return a + b

 

print add(2,3)

 

  

#匿名函数

add = lambda a,b : a + b

print add(2,3)

 

 

#========输出===========

5

5

 匿名函数的命名规则,用lamdba 关键字标识,冒号(:)左侧表示函数接收的参数(a,b) ,冒号(:)右侧表示函数的返回值(a+b)。

  因为lamdba在创建时不需要命名,所以,叫匿名函数 

 

5.列表解析

基于对运行在当前python下的测试,map调用比等效的for循环要快两倍,而列表解析往往比map调用要稍快一些

[ line.rstrip().upper() for line in open('script1.py')]

[ line.split() for line in open('script1.py')]

[ line.replace(' ','!') for line in open('script1.py')]

[('sys' in line, line[0]) for line in open('script1.py')]

 

五、重访迭代器:生成器

可以送回一个值并随后从其推出的地方继续的函数。这样的函数叫做生成器函数,因为它们随着时间产生值的一个序列
yield

用send方法,编写一个能够被它的调用者终止的生成器。

1.生成器表达式:迭代器遇到列表解析

至少在一个函数的基础上,编写一个列表解析基本上等同于:在一个list内置调用中包含一个生成器表达式以及迫使其一次生成列表中的所有结果。

2.生成器是单迭代器对象

对于生成器函数来说,也是如此,基于语句的def等价形式只支持一个活跃的生成器并且在一次迭代之后用尽。

3.计时脚本

注:我们必须通过内置的list调用来运行生成器表达式和函数结果,从而迫使它们生产其所有的值;如果没有这么做,可能会得到并没有真正工作的生成器。在(仅在)python3.0中,我们必须对map结果做同样的事情,因为它现在也是一个可迭代对象。

猜你喜欢

转载自blog.csdn.net/Zhou_ZiZi/article/details/81172276