Python 第四章 函数

1.函数入门

  • 定义:def 函数名(形参列表):

    ​ //可执行语句

    ​ [return [返回值]]

  • 调用:函数名(实参1,实参2。。)

2.递归函数

在一个函数体内调用自身被称为递归。

3.函数的参数

按照形参位置传入的参数叫位置参数,必须按照定义函数时指定的顺序传入参数值。

如果根据参数名来传入参数值,可以不按顺序,叫关键字参数。

关键字参数必须在位置参数的后面。

在形参前面加一个*,意味着该参数可以接收多个实参,会被当做元组放入;当把个数可变的形参放在前面时,后面必须使用关键字参数。

使用**的关键字参数收集成字典

def test(x,y,z=3,*books,**scores):
    print(x,y,z)
    print(books)
    print(scores)
test(1,2,3,'A','B',语文=89,数学=98)
# 输出
1 2 3
('A', 'B')
{'语文': 89, '数学': 98}

4.逆向参数收集

指在程序已有列表、元组、字典等对象的前提下,把他们的元素拆开后传给函数的参数

def foo(name,*nums):
    print(name)
    print(nums)
my_tuple = (1,2,3)
foo('fkit',my_tuple)
# 输出
fkit
((1, 2, 3),)
foo(*my_tuple)
# 输出
1
(2, 3)

5.变量作用域

每个函数执行时,系统都会为该函数分配一块临时内存空间,所有的局部变量都被保存在这块临时内存空间。当函数执行完,这块内存空间就被释放,局部变量也就失效了,因此离开函数后就不能在访问局部变量了。

全局变量意味着在所有函数中都能被访问。

变量和它的值就像一个字典。

​ Python提供以下3种函数获取指定范围内的变量字典。

  • 局部变量:在函数中定义的变量,包括参数,都被称为局部变量。
  • 全局变量:在函数外面、全局范围内定义的变量,叫全局变量。
  • globals():该函数返回全局变量内所有变量组成的变量字典。
  • locals():返回当前局部范围内所有变量组成的变量字典。如果在全局范围内调用locals函数,同样会获得全局范围内所有变量组成的变量字典。
  • vars(object):获取在指定对象范围内所有变量组成的变量字典。
  • 通过locals获取的局部范围内的变量字典,即使对他修改也不会影响局部变量
def test():
    age = 20
    # 访问函数局部范围内的变量数组
    print(locals()) # {'age':20}
    # 通过函数局部范围内的变量数组访问age变量
    print(locals()['age']) # 20
    # 修改变量的值
    locals()['age'] = 12
    print('xxx',age) # 依然输出20
    globals()['x'] = 19
test()
x = 5
y = 20
print(globals()) # {...,'x':5,'y':20}
print(locals()) # {...,'x':5,'y':20}
print(x) # 5
print(globals()['x']) # 5
globals()['x'] = 39
print(x) # 39
locals()['x'] = 99
print(x) # 99

全局变量默认可以在所有函数内被访问,但如果在函数中定义了与全局变量同名的变量,此时就会发生局部变量遮蔽全局变量的情形。例如

name = 'Jim'
def test():
    # 直接访问全局变量
    print(name) # Jim
    # name = 'Kyrie'  
test()
print(name)  # Jim

如果加了第五行会报错,python语法规定:在函数内部对不存在的变量赋值时,默认就是重新定义了新的局部变量,这里就会把全局变量name遮蔽,所以会报错,说name变量还未定义。

有以下两种方法:

  • 访问被遮蔽的全局变量

    如果程序希望第四行能访问name全局变量,且在之后能重新定义name局部变量——也就是在函数中可以访问被遮蔽的全局变量,可以通过globals函数实现:

    name = 'Jim'
    def test():
        # 通过函数访问全局变量
        print(globals()['name']) # Jim
        name = 'Kyrie'  
    test()
    print(name)  # Jim
    
  • 在函数中声明全局变量

    为了避免在函数中对全局变量赋值(不是重新定义局部变量),可使用global语句声明全局变量:

    name = 'Jim'
    def test():
        # 声明name是全局变量,后面的赋值语句不会重新定义局部变量
        global name
        print(name) # Jim
        name = 'Kyrie'  # 修改了全局变量
    test()
    print(name)  # Kyrie
    

6.局部函数

局部函数:放在函数体内定义的函数。

默认情况下,局部函数对外部是隐藏的,局部函数只能在其封闭函数内有效,其封闭函数也可以返回局部函数,以便程序在其他作用域中使用局部函数。

def get_math_func(type,nn):
    def square(n):
        return n*n
    def cube(n):
        return n*n*n
    def factorial(n):
        result = 1
        for index in range(2,n+1):
            result *= index
        return  result

    if type == 'square':
        return square(nn)
    elif type == 'cube':
        return cube(nn)
    else:
        return factorial(nn)
print(get_math_func('square',3)) # 9
print(get_math_func('cube',3))   # 27
print(get_math_func('',3))   # 6

局部函数内的变量也会遮蔽它所在函数内的局部变量;python提供nonlocal关键字声明访问赋值语句只是访问该函数所在函数内的局部变量。

7.函数高级内容

  • 使用函数变量

  • 使用函数作为函数形参

    代码需要动态改变,即fn会改变

    def map(data, fn):
        result = []
        for e in data:
            result.append(fn(e))
        return result
    def square(n):
        return n * n
    def cube(n):
        return n * n * n
    def factorial(n):
        result = 1
        for index in range(2,n+1):
            result *= index
        return  result
    data = [3,4,9,5,8]
    print(data)   #[3, 4, 9, 5, 8]
    print(map(data,square))  #[9, 16, 81, 25, 64]
    
  • lambda表达式

    语法格式:lambda x,y:x+y

    冒号左边的是参数列表,右边是表达式的返回值。

  • 计算代码计算时间

    import time
    
    start = time.time() # 可以获取当前的时间戳
    # 时间戳是从1970-01-01 00:00:00 UTC ~现在的秒数
    print('start = ',start)  # start =  1615089126.3489187 是现在UTC的秒数,要减8个小时
    
    x = 0
    for i in range(1,100000000):
        x +=i
    print(x)
    
    end = time.time()
    
    print('代码耗时{}秒'.format(end-start))
    
    #start =  1615089358.4768794
    #4999999950000000
    #代码耗时7.5029296875秒
    

    优化:

    def cal_time(fn):
        start = time.time()
        fn()
        end = time.time()
        print('代码耗时{}秒'.format(end-start))
    
    def demo():
        x = 0
        for i in range(1,100000000):
            x +=i
        print(x)
    
    cal_time(demo)
    

8.装饰器

import time
def cal_time(fn):
    print('首先调用外部函数。')
    def inner():
        start = time.time()
        fn()
        end = time.time()
        print('代码耗时{}秒'.format(end-start))
        
    return inner

@cal_time # 首先会调用cal_time,然后把被装饰的函数传递给fn
def demo():
    x = 0
    for i in range(1,100000000):
        x +=i
    print(x)
# 当再次调用demo函数,它是cal_time函数的返回值inner
demo()

装饰器详解:

def funA(fn):
    print('A')
    fn() #执行传入的fn参数
    return 'fkit'
'''
下面的装饰效果相当于funA(funB)
funB将会被替换(装饰)成该语句的返回值
由于funA函数返回fkit,因此funB就是fkit
'''
@funA    # 相当于funA(funB)
def funB():
    print('B')
print(funB) # funB被替换成fkit
# 输出
A
B
fkit

当程序使用@函数(比如函数A)装饰另一个函数,比如函数B,实际上完成以下两步:

  • 将被修饰的函数(函数B)作为参数传给@符号引用的函数,比如函数A。
  • 将函数B替换成第一步的返回值。
def foo(fn):
    # 定义一个嵌套函数
    def bar(*args):
        print('===1===', args)
        n = args[0]
        print('===2===', n * (n - 1))
        # 查看传给foo函数的fn函数
        print(fn.__name__)
        fn(n * (n - 1))
        print("*" * 15)
        return fn(n * (n - 1))

    return bar
'''
下面的装饰效果相当于foo(my_test)
my_test将会被替换成该语句的返回值,也就是bar函数
'''
@foo
def my_test(a):
    print("==my_test函数==", a)
# 打印my_test函数,将会看到就是bar函数
print(my_test)  # <function foo.<locals>.bar at 0x00000264804C09D0>
# 下面代码看上去是调用my_test函数,实际是调用bar函数
my_test(10)
my_test(6, 5)
# 输出结果
<function foo.<locals>.bar at 0x00000264804C09D0>
===1=== (10,)
===2=== 90
my_test
==my_test函数== 90
***************
==my_test函数== 90
===1=== (6, 5)
===2=== 30
my_test
==my_test函数== 30
***************
==my_test函数== 30

上面程序定义了一个装饰器foo函数,该函数执行完返回bar函数,这意味着该@foo修饰的函数最终都会被替换为bar函数。

上面程序使用@foo修饰my_test函数,因此程序同样会执行foo(my_test),并将my_test替换成foo函数的返回值bar函数。

posted @ 2021-03-24 21:27  KKKyrie  阅读(115)  评论(0编辑  收藏  举报