猪行天下之Python基础——5.1 函数(上)

内容简述:

  • 1、函数定义
  • 2、形参与实参
  • 3、关键字参数与默认参数
  • 4、可变参数
  • 5、全局变量与局部变量
  • 6、内部函数
  • 7、闭包
  • 8、lambda表达式
  • 9、递归

1、函数定义

我们可以将一些实现特定功能重复使用到的「代码片段」抽取出来,封装成一个函数。比如求两个数和的函数:

def plus(a, b):
    """
    计算两个数的和
    :param a: 第一个参数
    :param b: 第二个参数
    :return: 两个参数的和
    """

    return a + b

if __name__ == '__main__':
    print("1 + 2 = %d" % plus(12))
复制代码

运行结果如下

1 + 2 = 3
复制代码

从上面的求和函数可以初窥函数定义的一些端倪,接着具体说下规则:

  • 函数定义格式def 函数名(传入参数)
  • 使用 `return` 返回值,不写的话默认返回 None 值。
  • Python函数的 返回值可以有多个 ,本质上利用的元组
  • Python传递的 参数有多个 的话,可以用逗号隔开
  • 一个建议:函数定义时,可在函数的第一行语句中选择性地使用文档字符串编写函数说明,除了方便阅读,使用help(函数名)也可以拿到这个函数的说明信息。

2、形参与实参

定义函数时,函数名后传入的参数叫「形参」,调用函数时,函数名后传入的参数叫「实参」。这里还涉及到一个「传值与传址」的问题,传值就是传入一个参数的值,而传址则是传入一个参数的内存地址。两者的区别是:如果函数是传值类型的,在函数里修改了参数的值,外部的变量(实参)是不会改变的,比如下面这样一段代码:

def f(a):
    a = 1

b = 0
f(b)
print(b)
复制代码

运行结果如下

0
复制代码

尽管我们修改了传入参数的值,但是实参却依旧是0,没有改变,这就是传参,如果传递的是内存地址,修改的了话实参也会受影响。但是Python和其他编程语言有点不同(比如C语言里可以用&参数名传地址)。

Python不允许开发者选择采用传值还是传址,而是采用「传对象引用」的方式,如果传入的参数是一个不可变对象数字,字符串和元组)的引用,就不能修改原始对象的值;如果传入的参数是一个可变对象列表,字典)的引用,就能直接修改原始对象的值。

比如下面这样一串代码:

def f(a):
    b[0] = 1

b = [0]
f(b)
print(b)
复制代码

运行结果如下

[1]
复制代码

3、关键字参数与默认参数

关键字参数」:当函数需要传入的参数有多个的时候,怕参数混淆传错,可以在传入的时候指定形参的参数名,比如:plus(a = 1, b = 2)

默认参数」:在 定义形参的时候赋予初始值,调用的时候就可以不带参数去调用函数,比如:
def plus(a=1, b = 2),调用的时候直接plus()或者只传入一个参数plus(3)都是可以的,还可以配合关键字参数指定传入的是哪个参数。另外,默认参数也称作「缺省参数」。

4、可变参数

有时传入函数中的 参数数目 可能是 不固定 的,比如,要你计算一堆数字的和,而具体有多少
个数字不知道,这个时候就可以使用可变参数了。只需要在函数定义时在参数前加* 星号,
就代表这个参数是可变参数(其实是只是把数据打包成了一个元组)。

另外,如果除了可变参数外还有其他的参数,那么写在可变参数后的参数要用关键字参数指定
否则会加入可变参数的范畴。还有一点要注意,如果传入的参数是列表或者元组,会被再次
打包成元组,如果想解包的话,需要在实参前加*,代码示例如下:

def plus(*a):
    result = 0
    for b in a:
        print(b, end='\t')

if __name__ == '__main__':
    a = [12345]
    plus(a)
    print()
    plus(*a)
复制代码

运行结果如下

[12345
1   2   3   4   5 
复制代码

另外,如果想把参数打包成字典的方式,可在函数形参前使用两个**标识


5、全局变量与局部变量

全局变量:定义在最外部,可在函数内部进行访问,但不能直接修改
局部变量:定义在函数内部,在函数外部无法访问的参数和变量。

局部变量无法在外部访问的原因

Python在运行函数时,会利用栈(Stack)来存储数据,执行完函数后,所有数据会被自动删除。

函数中无法修改全局变量的原因

试图在函数里修改全局变量的值时,Python会自动在函数内部新建一个名字一样的局部变量代替。如果硬是要修改,可以在函数内部使用global关键字修饰全局变量,但是不建议这样做,会使得程序维护成本的提高。


6、内部函数

所谓的内部函数其实就是「函数嵌套」,在一个函数中嵌套另一个函数,要注意:

内部函数的作用域,只在内部函数的「直接外部函数内」,外部是无法调用的没,外部调用内部函数会直接报:函数找不到的错误!

内部函数无法直接修改外部函数中的变量,否则会报UnboundLocalError错误!如果想在内部函数中直接修改,可以把直接外部函数中的变量通过容器类型来存放,或者使用Python提供的 nonlocal关键字 修饰。代码示例如下:

def fun_x():
    x = [10]
    y = 10
    def fun_y():
        x[0] += x[0]
        nonlocal y
        y *= y
        return x[0] * y
    return fun_y()

if __name__ == '__main__':
    print(fun_x())
复制代码

运行结果如下

2000
复制代码

7、闭包

在函数内嵌套了另一个函数,如果「内部函数引用了外部函数的变量」,则可能产生闭包。
Python中形成闭包的三个条件:

  • 函数嵌套
  • 内部函数引用外部变量
  • 外部函数返回内部函数

一个函数闭包的代码示例如下:

def outer(a):
    b = 1
    def inner():
        print(a + b)
    return inner

if __name__ == '__main__':
    test_1 = outer(2)
    test_1()
复制代码

运行结果如下

3
复制代码

在上面的代码中,直接把内部函数当做返回值返回了,b是一个局部变量,按理来说,生命周期在调用完outer()函数后就完结了。但是载上面的代码中,调用test_1时,b变量的值却正常输出了,函数闭包使得函数的「局部变量信息」得以保存。

Python中通过__closure__属性保存闭包中的局部变量,把上面test_1函数里的东东
打印出来,代码如下:

print(test_1.__closure__)
print(test_1.__closure__[0].cell_contents)
print(test_1.__closure__[1].cell_contents)
复制代码

运行结果如下

(<cell at 0x000001D09ACF85E8int object at 0x00000000667D6C30>, <cell at 0x000001D09ACF8648int object at 0x00000000667D6C10>)
2
1
复制代码

8、lambda表达式

在Python中可以使用lambda关键字来创建匿名函数直接返回一个函数对象,而不用去纠结给函数起什么名字,省去了定义函数的步骤从而简化代码,一个对比大小简单的lambda表达式代码示例如下:

big = lambda x, y: x > y
print("第一个参数比第二个参数大:%s" % big(1, 2))
复制代码

运行结果如下

第一个参数比第二个参数大:False
复制代码

9、递归

所谓的递归就是「函数调用自身」,最简单的递归求和代码示例如下:

def sum(n):
    if n == 1:
        return 1
    else:
        return n + sum(n - 1)

print("1到100的求和结果是: %d" % sum(100))
复制代码

运行结果如下

1到100的求和结果是: 5050
复制代码

另外要注意两点

  • 递归要有结束条件,以避免递归的无休止调用!
  • 递归可以简化程序,但不一定能提高程序的执行效率!

如果本文对你有所帮助,欢迎
留言,点赞,转发
素质三连,谢谢?~


转载于:https://juejin.im/post/5ca852ff5188254401191aa7

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值