python基础语法 - 函数进阶

本章内容索引:

  • 高阶函数
  • 递归函数
  • 函数作用域
  • 装饰器
  • 内置函数

高阶函数

1.函数在内存种的储存情况

2.基本语法

def f(n):
    return n*n

def foo(a,b,f):
    return f(a) + f(b)

print(foo(2,4,f))
  • 函数名是一个变量
  • 函数名可以进行赋值
  • 函数名可以作为函数参数,还可以作为函数的返回值

递归函数

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

实例1(阶乘)

5!=5*4*3*2*1=120

def f(n):
    if n == 1:
        return 1
    return n*f(n-1)

f(5) #120

实例2(斐波那契数列)

1, 1, 2, 3, 5, 8, 13, 21, 34, 55

def fibo(n):
    if n <= 1:
        return n
    return (fibo(n - 1) + fibo(n - 2))

fibo(10) #55

递归特性:

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

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

3. 递归效率不高,递归层次过多会导致栈溢出

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

函数作用域

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

  • L:local,局部作用域,即函数中定义的变量;
  • E:enclosing,嵌套的父级函数的局部作用域,即包含此函数的上级函数的局部作用域,但不是全局的;
  • G:globa,全局变量,就是模块级别定义的变量;
  • B:built-in,系统固定模块里面的变量,比如int, bytearray等。 搜索变量的优先级顺序依次是:作用域局部>外层作用域>当前模块中的全局>python内置作用域,也就是LEGB。
x = int(2.9)  # int built-in
 
g_count = 0  # global
def outer():
    o_count = 1  # enclosing
    def inner():
        i_count = 2  # local
        print(o_count)
    # print(i_count) 找不到
    inner() 
outer()
 
# print(o_count) #找不到

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

2.作用域的产生

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

if 2>1:
    x = 1
print(x)  # 1

这个是没有问题的,if并没有引入一个新的作用域,x仍处在当前作用域中,后面代码可以使用。

def test():
    x = 2
print(x) # NameError: name 'x2' is not defined

def、class、lambda是可以引入新作用域的。 

3.global关键字

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

count = 10
def outer():
    global count
    print(count) 
    count = 100
    print(count)
outer()
#10
#100

4.nonlocal关键字

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

5.作用域小结

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

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

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

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

装饰器

1.作用域

  • L_E_G_B

2.高阶函数

  • 函数名可以作为参数输入
  • 函数名可以作为返回值

3.闭包

定义:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)

def outer():
    x=10
    def inner(): #条件1 inner就是内部函数
        print(x) #条件2 外部环境的一个变量
    return inner #结论 内部函数inner就是一个闭包

f = outer()
f()

  关于闭包:闭包=内部函数块+定义函数时的环境

4.装饰器

def foo():
    print('foo...')
    time.sleep(1)

def show_time(f):
    strat = time.time()
    f()
    end = time.time()
    print('spend %s' % (end - strat))

show_time(foo)
  • 改变了原函数的调用方式

解决方式如下:

def foo():
    print('foo...')
    time.sleep(1)


def show_time(f):

    def inner():
        strat = time.time()
        f()
        end = time.time()
        print('spend %s' % (end - strat))

    return inner

foo = show_time(foo)

foo()
  • 每个函数都需要重新赋值

解决方法如下:(语法糖)

def show_time(f):
    def inner():
        strat = time.time()
        f()
        end = time.time()
        print('spend %s' % (end - strat))
    return inner

@show_time #foo = show_time(foo)
def foo():
    print('foo...')
    time.sleep(1)

foo()

功能函数加参数:

不定长参数:

def show_time(f):
    def inner(*a,**b):
        strat = time.time()
        f(*a,**b)
        end = time.time()
        print('spend %s' % (end - strat))
    return inner

@show_time #foo = show_time(foo)
def foo(*a,**b):
    sums = 0
    for i in a:
        sums += i
    print(sums)
    time.sleep(1)

foo(2,3,4,6)

装饰器加参数:

内置函数:

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的迭代器返回。可以看作是过滤函数。

2. map(function, sequence) 

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也支持相应数量的参数输入

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 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在创建时不需要命名,所以,叫匿名函数 

 

posted @ 2018-02-06 12:21  皮蛋小顽童  阅读(170)  评论(0编辑  收藏  举报