(八)函数

itmahy
itmahy
发布于 2024-01-19 / 39 阅读
0
0

(八)函数


函数也是一个对象。函数可以用来保存一些可执行的代码块,实现多次调用

1. 创建函数

def 函数名([形参1, 形参2, 形参3, ..., 形参n]):

函数名可以包含数字,字母,下划线,汉字但是不能以数字开头

def test_01():
    print('这是 test_01 函数')
# 调用
test_01()
​
>>>结果:
这是 test_01 函数
================================
print(type(test_01), id(test_01))
​
>>>结果:
<class 'function'> 1879503552032
===============================
# 定义一个函数,求两个数的和
def num_sum(num1, num2) :
    print(num1 + num2)
    
num_sum(1,2)
​
>>>结果:
3
==================================
print(函数名(**param)) # 这是打印函数的返回值
print(函数名) # 这是打印函数的对象
>>>结果:
返回值
<function 函数名 at 0x000001E6A0A03E20>

2. 参数

定义函数时,可以指定形参,可以是多个形参,也可以没有形参,形参使用 ,隔开。

  • 形参:形式参数。定义形参相当于在函数内部声明了变量,但是不赋值,在创建函数的时候声明

    指定形参时,可以为形参执行默认值,如果调用时传递了该参数,则该默认值不生效,如果没有传递该参数,默认值就生效了。

    def test_02(a, b = 2, c = 3) :
        print(a,b,c)
    ​
    test_02(5)
    ​
    >>>结果:
    5 2 3
    • 在定义函数时,可以在形参前面加一个星号,这样这个形参会获得所有实参(称之为参数的装包)

      def test_03(*a) :
          print('a = ',a,'\t type:' , type(a))
      test_03()
      ​
      >>>结果:
      a =  ()          type: <class 'tuple'>
      =====================================================
      def test_03(*a) :
          result = 0
          for i in a :
              result += i
          
          print(result)
          
      test_03(1,2,3,4,5,6,7,8)
      ​
      >>>结果:
      36

      注意事项:带 *的参数只能有一个,并且可以和其他参数配合使用,在传递其他参数时,必须以关键字的形式传递实参。如果在参数最前面写了一个*,表示所有的参数都必须使用关键字传递实参,并且 ·*a只接收位置参数,不接收关键字参数。**a可以接收其他关键字参数,他会将参数存储到一个字典中,key 是参数名称,value 是参数的值。**a只能有一个,并且只能写在所有函数的最后面

      def test_03(*a,b,c) :
          result = 0
          for i in a :
              result += i
          print(result,b,c)
      test_03(1,2,3,4,5,6,b = 7,c = 8)
      >>>结果:
      21 7 8
      ==========================================================
      def test_03(**a) :    
          print(a,type(a))
      ​
      test_03(a=1,b=2,c=3,d=4)
      >>>结果:
      {'a': 1, 'b': 2, 'c': 3, 'd': 4} <class 'dict'>
      ==========================================================
      def test_03(e,f,**a) :    
          print(a,type(a),e,f)
      test_03(1,2,a=1,b=2,c=3,d=4)
      >>>结果:
      {'a': 1, 'b': 2, 'c': 3, 'd': 4} <class 'dict'> 1 2

  • 实参:实际参数。实参是给形参赋值的,在调用的时候传递,实参可以传递任意类型的参数。在调用函数时,解释器不会检查实参的类型。

    • 位置参数:就是将对应位置的实参赋值给对应位置的形参,第一个实参赋值给第一个形参,以此类推。这是我们正常使用的

    • 关键字参数:可以不按照形参定义的顺序传递,而是根据参数名称去传递参数

      def test_02(a, b = 2, c = 3) :
          print(a,b,c)
      test_02(c =5,b =10,a = 23)
      >>>结果:
      23 10 5
    • 位置参数可以跟关键字参数混合使用,必须将位置参数写到前面

      def test_02(a, b = 2, c = 3) :
          print(a,b,c)
      test_02(45, c = 34)
      >>>结果:
      45 2 34
    • 当参数是一个对象的时候,如果在函数内部修改了对象的值,那么也会影响函数外面的变量

    • 参数的解包

      传递实参时,也可以在序列类型的参数前面添加*,这样就可以自动将序列中的元素依次作为参数传递,要求序列中元素的个数必须跟参数的个数保持一致。

      def test_04(a, b, c) :
          print(a,b,c)
      param = (10,20,30) # 这里可以是元组,列表等
      test_04(*param)
      >>>结果:
      10 20 30

      也可以使用**给字典解包

      def test_04(a, b, c) :
          print(a,b,c)
      param = {'a' : 10, 'b' : 20, 'c' : 30}
      test_04(**param)
      >>>结果:
      10 20 30

3. 返回值

返回值就是函数执行结束之后的结果。可以通过return指定返回值。

如果没有写 return 或者仅仅写了一个 return ,相当于 return None

def test_04(a, b, c) :
    return a + b +c

param = {'a' : 10, 'b' : 20, 'c' : 30}
result = test_04(**param)
print(result)
>>>结果:
60
  • 在函数中,return 后面的代码都不会执行,函数会自动结束

4. 文档字符串(doc str)

在定义函数时,可以在函数内部编写文档字符串,文档字符串就是函数的使用说明。可以通过help()来查看函数的使用说明

在函数内部的第一行书写一个字符串就是文档字符串。

def test_04(a, b, c) :
    '''
    这是一个测试函数。
        a, 类型,默认值,作用等等
        a, 类型,默认值,作用等等
        a, 类型,默认值,作用等等
    '''
    return a + b +c

help(test_04)
>>>结果:
Help on function test_04 in module __main__:

test_04(a, b, c)
    这是一个测试函数。
        a, 类型,默认值,作用等等
        a, 类型,默认值,作用等等
        a, 类型,默认值,作用等等
================================================
def test_04(a:int, b:int, c:int) -> int:
    '''
    这是一个测试函数。
        a, 类型,默认值,作用等等
        a, 类型,默认值,作用等等
        a, 类型,默认值,作用等等
    '''
    return a + b +c
# 这个说明了 参数都是int 类型, ->int 表示 返回值为int类型

5. 作用域(scope)

指的是变量生效的区域。

一共有两种作用域

  • 全局作用域:在程序执行时创建,在程序结束时销毁,所有函数以外的区域都是全局作用域,变量叫做全局变量,可以在程序任意位置访问

  • 函数作用域: 在函数调用时创建,在函数结束时销毁。函数每被调用一次,就会产生新的函数作用域,在函数作用域中定义的变量,都是局部变量,只能在函数内部被访问。

  • 使用变量时,会优先在当前作用域中寻找该变量,如果有则使用,如果没有,则继续去上一级作用域中寻找,以此类推。

  • 如果希望在函数内部修改全局变量,则需要使用 global 关键字声明变量,然后修改就会修改全局变量。

    d = 10
    def test_04(a:int, b:int, c:int) -> int:
        '''
        这是一个测试函数。
            a, 类型,默认值,作用等等
            a, 类型,默认值,作用等等
            a, 类型,默认值,作用等等
        '''
        d = 20
        print(d)
        return a + b +c
    test_04(1,2,3)
    print(d)
    
    >>>结果:
    20 
    10 # 表示没有修改全局变量,输出的还是 10
    ===================================================
    d = 10
    
    def test_04(a:int, b:int, c:int) -> int:
        '''
        这是一个测试函数。
            a, 类型,默认值,作用等等
            a, 类型,默认值,作用等等
            a, 类型,默认值,作用等等
        '''
        global d # 使用global 生命变量,就可以修改全局变量了
        d = 20 
        print(d)
        return a + b +c
    test_04(1,2,3)
    print(d)
    
    >>>结果:
    20
    20 # 表示修改全局变量,输出是修改之后的值

6. 命令空间(namespace)

命令空间指的是变量存储的位置,每一个变量都需要存储到指定的命名空间中,每一个作用域都有一个对应的命名空间

全局命名空间保存全局变量,函数命名空间保存局部变量

命名空间其实就是一个字典,是一个专门存储对象的字典

使用locals()函数获取当前作用域的命名空间,返回的是一个字典

如果在全局作用域中执行locals(),则获取全局命名空间,如果在函数作用域中执行locals(),则获取函数命名空间

d = 10
def test_04(a:int, b:int, c:int) -> int:
    '''
    这是一个测试函数。
        a, 类型,默认值,作用等等
        a, 类型,默认值,作用等等
        a, 类型,默认值,作用等等
    '''
    global d
    d = 20
    print(d)
    return a + b +c
scope = locals()
print(scope)

>>>结果:
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001D38BE54A90>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'XXX.py', '__cached__': None, 'd': 20, 'test_04': <function test_04 at 0x000001D38BD93E20>, 'scope': {...}}

通过操作locals()的返回值,可以获取变量或者添加变量(一般不建议这么操作)

scope = locals()
print(scope['d'])
scope['aa'] = 20 
print(scope['aa'])
>>>结果:
10
20

可是使用global()函数,在任意位置获取全局命名空间。

7. 递归

递归式函数就是在函数中自己调自己

求 10 的阶乘

# 求 10 的阶乘
def test_05(n) :
    result = n
    for i in range(1, n) :
        result *= i
    return result

print(test_05(10))
>>>结果:
3628800
===========================================
#使用递归
# 10! = 10 * 9!
# 9!  = 9 * 8!
# 8! = 8 * 7!
# ...
# 2! = 2 * 1!
# 1! = 1

def test_05(n) :
    if n == 1:
        return 1
    return n * test_05(n -1)
print(test_05(10))

>>>结果:
3628800
============================================
# 求 n 的 i 次幂
def test_06(n, i) :
    if i == 1:
        return n
    return n * test_06(n, i -1)

print(test_06(2,8))
>>>结果:
256
============================================
# 检查是否为回文
def hui_wen(param_str:str) :
    if len(param_str) < 2 :
        return True
    elif param_str[0] != param_str[-1] :
        return False
    return hui_wen(param_str[1 : -1])

print(hui_wen('abcba'))
>>>结果:
True

8. 高阶函数

函数式编程,在 Python 中,函数是一等对象

一等对象有以下特点:

  • 对象是在运行时创建

  • 能赋值给变量或作为数据结构中的元素

  • 能作为参数传递

  • 能作为返回值

高阶函数:接收函数作为参数,或者将函数作为返回值的函数就是高阶函数

  • 接收一个或者多个函数作为参数

  • 将函数作为返回值返回

  • 当使用一个函数作为参数时,实际上是将指定的代码传递进了目标函数

    def fn2(i) : 
        '''
            检查是否为偶数
        '''
        if i % 2 == 0 :
            return True
        return False
    
    def fn3(i) : 
        '''
            检查是否为奇数
        '''
        if i % 2 != 0 :
            return True
        return False
    
    def fn(func, n) :
    
        result_list = []
        
        for i in n :
            if func(i) :
                result_list.append(i)
        
        return result_list
    
    li = [1,2,3,4,5,6,7,8,9,10]
    # 这里将函数作为参数进行传递
    print(fn(fn2, li))
    
    >>>结果:
    [2, 4, 6, 8, 10] # 这里传参了函数 fn2

9. 匿名函数

filter(function,list)过滤器。可以从序列中过滤出符合条件的数据,保存到一个新的序列中

  • 参数

    • 函数,根据该函数来过滤序列(可迭代的结构)

    • 需要过滤的序列(可迭代的结构)

  • 返回值:过滤后的新序列

    def fn2(i) : 
        '''
            检查是否为偶数
        '''
        if i % 2 == 0 :
            return True
        return False
    
    def fn3(i) : 
        '''
            检查是否为奇数
        '''
        if i % 2 != 0 :
            return True
        return False
    
    li = [1,2,3,4,5,6,7,8,9,10]
    
    print(filter(fn2, li))
    print(list(filter(fn2, li)))
    
    >>>结果:
    <filter object at 0x0000024D4A5FBC40>
    [2, 4, 6, 8, 10]

上面的不推荐使用,建议使用匿名函数

lambda表达式

用来创建一些简单函数

语法: lambda 参数列表 : 返回值

lambda a,b : a + b 调用 (lambda a,b : a + b)(10,20)

# 声明
lambda a, b : a + b
# 调用
print((lambda a, b : a + b)(10,29))

>>>结果:
39
============================================
# 声明
fn5 = lambda a, b : a + b
# 调用
print(fn5(10 ,20))

>>>结果:
30
============================================
li = [1,2,3,4,5,6,7,8,9,10]
print(list(filter(lambda n : n % 2 == 0,li)))
>>>结果:
[2, 4, 6, 8, 10]
  • map(function,list):map 函数可以对可迭代对象中的元素做指定的操作,然后将其添加到一个新的对象中返回

    li = [1,2,3,4,5,6,7,8,9,10]
    print(list(map(lambda n : n  + 1,li)))
    >>>结果:
    [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

匿名函数一般作为参数使用,其他地方一般不使用。

  • sort()函数时排序函数。默认是使用 < 比较的。sort可以接收一个关键字参数,key 需要函数作为参数,当设置了此函数,每次都会以列表中的一个元素作为参数来调用此函数,并且使用函数的返回值来比较元素的大小

    li1 = ['sd', 'sdfsdfs', 'aaa', 'wsdrsdfsdfsdfsdf']
    li1.sort(key = len, reverse = True)
    print(li1)
    
    >>>结果:
    ['wsdrsdfsdfsdfsdf', 'sdfsdfs', 'aaa', 'sd']
  • sorted()用法跟 sort()基本一致,但是 sorted()可以对任意序列进行排序,并且不会影响原来的对象,而是返回一个新的对象

10. 闭包

将函数作为返回值返回

def fn1() :
    nums_list = []

    def fn2(n) :
        nums_list.append(n)
        return sum(nums_list)/len(nums_list)
    
    return fn2
        
l = fn1()
print(l(4))
print(l(6))
print(l(9))
print(l(10))
>>>结果:
4.0
5.0
6.333333333333333
7.25

11. 装饰器

下面的 fn函数就是个装饰器,可以直接调用,也可以使用 @装饰器 进行使用,装饰器可使用多个

def fn(old):
    '''
        用来对其他函数进行扩展
            参数:
                old: 要扩展的函数对象
    '''
    def fn2(*args, **kwargs):
        print('函数执行开始....')
        result = old(*args, **kwargs)
        print('函数执行结束....')
        return result
    
    return fn2

def add(a, b):
    return a + b

def say_hello():
    print('大家好...')

r = fn(add)
print(r)
c = r(2,4)
print(c)
r= fn(say_hello)
print(r)
print(r())

>>>结果:
<function fn.<locals>.fn2 at 0x000001D1AD21A170>
函数执行开始....
函数执行结束....
6
<function fn.<locals>.fn2 at 0x000001D1AD21A200>
函数执行开始....
大家好...
函数执行结束....
None
==============================================================
# 可以直接使用这个扩展函数
@fn
def fn3(a, b):
    return a * b

print(fn3(2,4))
>>>结果:
函数执行开始....
函数执行结束....
8



评论