• Python 从入门到进阶之路(四)


    之前的文章我们简单介绍了一下 Python 的几种变量类型,本篇文章我们来看一下 Python 中的函数。

    函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。

    函数能提高应用的模块性,和代码的重复利用率。你已经知道Python提供了许多内建函数,比如print()。但你也可以自己创建函数,这被叫做用户自定义函数。

    你可以定义一个由自己想要功能的函数,以下是简单的规则:

    • 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()
    • 任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。
    • 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
    • 函数内容以冒号起始,并且缩进。
    • return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。

    Python 定义函数使用 def 关键字,一般格式如下:

    1 def 函数名(参数列表):
    2     函数体
    1 def getNum():
    2     pass

    如果我们定义了一个函数,里面什么也没有写,为了不让代码崩溃,我们可以写一个 pass。

    现在我们来定义一个可执行的函数:

    1 def getNum():
    2     print(123)
    3 getNum()  # 123

    如上我们定义了一个 getNum 的函数,函数内部 print 输出 123,当我们调用 getNum() 时,就会输出 123。

    如果我们不想输出固定的 123,输出结果有我们自己定义,我们可以通过传参的形式解决:

    def getNum(num):
        print(num)
        print(num * 2)
    getNum(123)  # 123 246

    可以通过 return 的方式把想要的结果 return 出来:

    1 def getNum(num):
    2     return num
    3 print(getNum(123))  # 123

    函数中也可以使用嵌套函数的方式:

    1 def getNum(num):
    2     getName(num)
    3 
    4 def getName(name):
    5     print(name)
    6 
    7 getNum("zhangsan")  # zhangsan

    我们通过向函数传值来获取我们的结果,也可以通过设置变量的形式来解决:

    num = 1
    def getNum():
        print(num)
    
    getNum()  # 1

    上面的代码我们设置了一个全局变量,当然我们也可以在函数内部设置变量,叫做局部变量,这样更有利于我们对自己函数内的变量操作而不去改变全局的变量,如下:

    1 def getNum():
    2     name = "zhangsan"
    3     print(name)
    4 
    5 getNum()  # zhangsan

     在有的时候,我们可以直接将函数内的参数设置默认值,这就就可以避免在没有传入该参数时函数报错:

    1 def getNum(a, b, c=11, d=22):
    2     print(a)
    3     print(b)
    4     print(c)
    5     print(d)
    6 
    7 getNum(1, 2, 3)  # 1 2 3 22

    在上面的代码中,我们对函数 getNum 的第三个和第四个参数设置了默认值,在调用该函数时,我们传入了三个值,然后输出结果发现,第三个参数的默认值被调用 getNum 时传入的参数值覆盖掉了,而第四个参数值由于没有传入,所以使用了默认值 d=22。由此我们可以得出,当我们不传入对应参数值时如果设置默认值则使用默认值,如果传入了参数值则优先使用传入的参数值。

    在上面的代码中,我们定义的参数都是单一的字符串或者数字,我们也可以定义列表,元组,字典这样的参数,如下:

     1 list = [11, 22, 33]
     2 tuple = (44, 55, 66)
     3 dict = {"a": "77", "b": 88, "c": 99}
     4 
     5 def getTest(list, tuple, dict):
     6     print(list)
     7     print(tuple)
     8     print(dict)
     9 
    10 getTest(list, tuple, dict)
    11 '''
    12 [11, 22, 33]
    13 (44, 55, 66)
    14 {'a': '77', 'b': 88, 'c': 99}
    15 '''

    在上面的代码中我们可以看出,Python 中的不同变量类型都可以作为参数传入函数中。

    接下来我们再来看一下函数中缺省参数的 *args 和 **kwargs

    1 def getTest(a, b, c=123, *args):
    2     print(a)  # 11
    3     print(b)  # 22
    4     print(c)  # 33
    5     print(args)  # (44, 55, 66)
    6 
    7 getTest(11, 22, 33, 44, 55, 66)

    在上面的代码中,print(c) 结果为 33,这个我们再上面已经解释过了,但是我们在参数 c 后面又加入了一个 *args 的形参,然后我们在调用 getTest() 时向函数内部传入参数的个数为 6 个,而函数体 getTest() 接收的参数为 4 个,按我们上面叫的函数方法,理论上程序应该报错,然而程序却能运行,运行结果打印的第 4 个参数结果为一个元组,将形参按顺序对应的参数对应完毕之后剩余的内容组成一个元组输出。其中 *args 可以写成 *xxx,我们习惯上命名为 *args。

    在函数传参时,除了 *args 外还有一个 **kwargs,如下:

    1 def getTest(a, b, c=123, *args, **kwargs):
    2     print(a)  # 11
    3     print(b)  # 22
    4     print(c)  # 33
    5     print(args)  # (44, 55, 66)
    6     print(kwargs)  # {'name': 'zhangsan', 'age': 30}
    7 
    8 getTest(11, 22, 33, 44, 55, 66, name="zhangsan", age=30)

    上面的代码中,我们在函数 getTest() 后面又多家了一个形参 **kwargs,当我们在调用 getTest() 函数并传入参数时,我们传入了 name="zhangsan" 这样的键值对,这样在打印输出 **kwargs 时会转为一个字典类型的数据。其中 **kwargs 可以写成 **xxx,我们习惯上命名为 **kwargs。

    注意:在print() 输出时,*args 和 **kwargs 的 * 不需要写。

    根据上面的 *args 和 **kwargs,我们现在来向这样一个问题,加入我先在外面分别定义了一个元组类型变量 tuple 和字典类型数据 dict,然后调用 getTest() 函数时将变量传入,输出结果还是跟上面的结果一样,即下面的代码:

     1 def getTest(a, b, c=123, *args, **kwargs):
     2     print(a)  # 11
     3     print(b)  # 22
     4     print(c)  # 33
     5     print(args)  # ((44, 55, 66), {'name': 'zhangsan', 'age': 30})
     6     print(kwargs)  # {}
     7 
     8 tuple=(44,55,66)
     9 dict={'name': 'zhangsan', 'age': 30}
    10 getTest(11, 22, 33, tuple, dict)

    当我们还是按照上面的 *args 和 **kwargs 写法写时,发现参数 tuple 和 dict 被当做一个数据输出到了 *args 里,这是由于当我们传入 tuple 和 dict 时被当做了一个整体,我们可以在传入前先将其解构一下就可以了,如下:

     1 def getTest(a, b, c=123, *args, **kwargs):
     2     print(a)  # 11
     3     print(b)  # 22
     4     print(c)  # 33
     5     print(args)  # (44, 55, 66)
     6     print(kwargs)  # {'name': 'zhangsan', 'age': 30}
     7 
     8 tuple=(44,55,66)
     9 dict={'name': 'zhangsan', 'age': 30}
    10 getTest(11, 22, 33, *tuple, **dict)

    如上,我们在传入参数的时候传入 *tuple 和 **dict 就可以解决了,但是如果我们在传入参数时和调用时都不加 * 不就相当于把整个变量作为参数了吗?如下:

     1 def getTest(a, b, c=123, tuple, dict):
     2     print(a)
     3     print(b)
     4     print(c)
     5     print(tuple)
     6     print(dict)
     7 
     8 tuple=(44,55,66)
     9 dict={'name': 'zhangsan', 'age': 30}
    10 getTest(11, 22, 33, tuple, dict)  # SyntaxError: non-default argument follows default argument

    我们会发现不写 * 的话会报错,原因是当我们在函数中定义默认参数时,默认参数必须写在最后面,即 c=123 需写在最后面,如下:

     1 def getTest(a, b, tuple, dict, c=123):
     2     print(a)  # 11
     3     print(b)  # 22
     4     print(c)  # 33
     5     print(tuple)  # (44, 55, 66)
     6     print(dict)  # {'name': 'zhangsan', 'age': 30}
     7 
     8 tuple=(44,55,66)
     9 dict={'name': 'zhangsan', 'age': 30}
    10 getTest(11, 22, tuple, dict, 33)  

    但是如果参数中存在 * 的话默认参数不能写在最后面,如下:

     1 def getTest(a, b, *tuple, **dict, c=123):
     2     print(a)
     3     print(b)
     4     print(c)
     5     print(tuple)
     6     print(dict)
     7 
     8 tuple=(44,55,66)
     9 dict={'name': 'zhangsan', 'age': 30}
    10 getTest(11, 22, *tuple, **dict, 33)  # SyntaxError: invalid syntax

    由此我们得出函数的传参顺序为:参数,默认参数,*args,**kwargs。

    我们接下来看一下匿名函数。

    python 使用 lambda 来创建匿名函数。

    所谓匿名,意即不再使用 def 语句这样标准的形式定义一个函数。

    • lambda 只是一个表达式,函数体比 def 简单很多。
    • lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
    • lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。
    • 虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。

    lambda 函数的语法只包含一个语句,如下:

    lambda [arg1 [,arg2,.....argn]]:expression
    1 # 实现一个两数相加的匿名函数
    2 sum = lambda num1, num2: num1 + num2
    3 print(sum(10, 20))  # 30
  • 相关阅读:
    redis数据结构底层剖析学习笔记2
    redis数据结构底层剖析学习笔记1
    java正则表达式学习笔记
    springmvc sessionfilter 登录过滤器
    Java中JSON字符串与java对象的互换实例详解
    用9种办法解决 JS 闭包经典面试题之 for 循环取 i
    近期流行的JavaScript框架与主题
    JavaScript 中的 this 问题总结 !
    常见前端面试题及答案(下)
    常见前端面试题及答案(上)
  • 原文地址:https://www.cnblogs.com/weijiutao/p/10725453.html
Copyright © 2020-2023  润新知