Python学习8--函数

一、函数的定义

具有一个功能或者多个功能的代码块
函数的定义:可以重复使用,用来实现单一或者多个“功能”的代码段,以功能为单位;
函数的意义:提高程序的模块化管理,提高代码重复使用率;
函数的分类:内置函数(内建函数),自定义函数;

二、内置函数

可以直接调用而不需自定义的函数,使用时需要注意函数的参数、返回值等

print(abs(-100))
li = [1,5,3]
li.sort()
print(li)

输出:

100
[1, 3, 5]

三、自定义函数

1、语法格式

自定义函数格式:
def 函数名(【参数1,参数2】):
函数体
【return 返回值】
函数名:大写字母,小写字母、数字、_组合,注意不能以数字开头
函数占位符:pass
【】表示可有可无,函数只有被定义后才能被调用
用法如下:

def one():
    pass

2、参数的使用

1)位置参数(必须参数)
一旦定义了位置参数,除默认参数外,必须传入,且必须按照定义的顺序传入

def print_char(num,line_num):
    for i in range(line_num):
        print("*"*num)
print_char(2,3)

输出:

**
**
**

案例中num参数和line_num为位置参数,传入参数时必须按照顺序进行参数传递

2)默认参数
当调用函数的是,不传入参数,会使用默认参数的值进行赋值
当默认参数跟位置参数一起使用时,位置参数需要放在前面
定义格式为:

def 函数(参数名=默认参数值,...):
   pass

使用方式如下:

def print_char(num,line_num=3):
    for i in range(line_num):
        print("*"*num)
print_char(5)
print("===============")
print_char(5,2)

输出为:

*****
*****
*****
===============
*****
*****

当只传递num参数时,line_ num参数默认为3
当既传递了num参数又传入line_ num参数时,以传入参数为准。

当函数默认参数是可变类型时

def fun1(a=[]):
    a.append(20)
    print(a)

fun1()
fun1()
fun1([1,2,3])
fun1()

输出为:

[20]
[20, 20]
[1, 2, 3, 20]
[20, 20, 20]

对比默认参数为不可变类型时

def fun1(a=1):
    a+=1
    print(a)

fun1()
fun1()
fun1(2)
fun1()

输出为:

2
2
3
2

结论:默认参数,不能绑定可变类型。只能使用不可变数据类型。

定义函数后,每次编译函数都会为默认参数指定固定的内存地址,当使用可变参数传递时,执行函数的时候默认参数的值也在发生改变,而采用不可变的默认参数时,无论执行多少次,默认参数的值还是指向定义的值
在这里插入图片描述
3)命名关键字参数
格式:

def 函数名(* , 参数名,参数名)

*,后面的都是必须以 参数名=参数值
调用:如果定义了命名关键字参数,就必须以 参数名=参数值 传入参数

def print_char(num,*,line_num):
    for i in range(line_num):
        print("*"*num)
print_char(2,line_num=3)

输出:

**
**
**

命名关键字参数作用:
(1)增加程序可读性
(2)忽略参数的顺序
(3)当参数有默认值时,调用变得更简单
注意:
(1)如果定义了命名关键字参数,除了有默认值的情况,否则同位置参数一样,必须传入
(2)在顺序上,如果还有位置参数,那么命名关键字参数一定放在位置参数的后面

4)可变参数( args)*
可变参数是用来收集所有的位置参数,形成一个元组
用法如下:求n个数的平方和

def sum2(*args):
    sum = 0
    for i in args:
        sum+=i**2
    return sum
print(sum2(1,2,3,4,5))

输出:

55

定义函数时,*表示将收集到的参数打包成一个元组
当输入的参数本身就是元组或列表时,要使用 * 先进行解包,用法如下:

def sum2(*args):
    sum = 0
    for i in args:
        sum+=i**2
    return sum
s1 =[1,2,3,4,5]
print(sum2(*s1))

输出:

55

5) 关键字参数( kwargs)**
收集所有的 命名关键字参数,打包形成字典,传递给函数的形式参数
使用方法如下:

def register(num,name,**kwargs):
    print(num,name)
    for k,v in kwargs.items():
        print(k,v)
register(100,"张三")
register(101,"李四",gender = "男",height=180)

输出:

100 张三
101 李四
gender 男
height 180

同理,如果传入的参数是字典时,需要先使用**进行解包
使用如下:

def register(num,name,**kwargs):
    print(num,name)
    for k,v in kwargs.items():
        print(k,v)
register(100,"张三")
d1 = {"gender":"男","height":180}
register(101,"李四",**d1)

输出:

100 张三
101 李四
gender 男
height 180

6)位置参数 默认参数 命名关键字 可变参数 关键字参数总结
参数组合使用时需要注意的时:
(1)位置参数要放在最前面
(2)命名关键字参数不能和可变参数放一起使用
(3)如果希望在可变参数后面继续加参数,可以通过参数名=参数值的形式传递
(4)位置参数> 命名关键字参数/可变参数 > 关键字参数
相关规则验证如下:

def f1(a,b,c=0,*args,**kwargs):
    print("a={},b={},c={},args={},kwargs={}".format(a,b,c,args,kwargs))
def f2(a,b,c=0,*,d,**kwargs):
    print("a={},b={},c={},d={},kwargs={}".format(a,b,c,d,kwargs))
f1(1,2)
f1(1,2,3)
f1(1,2,3,4,5)
f1(1,2,3,4,5,x=99)

f2(1,2,d=3)
f2(1,2,-1,d=3,e=4,f=5)
f2(1,2,-1,e=4,f=5,d=3)

输出:

a=1,b=2,c=0,args=(),kwargs={}
a=1,b=2,c=3,args=(),kwargs={}
a=1,b=2,c=3,args=(4, 5),kwargs={}
a=1,b=2,c=3,args=(4, 5),kwargs={'x': 99}
a=1,b=2,c=0,d=3,kwargs={}
a=1,b=2,c=-1,d=3,kwargs={'e': 4, 'f': 5}
a=1,b=2,c=-1,d=3,kwargs={'e': 4, 'f': 5}

万能参数

def f(*args,**kwargs):

四、函数的返回值

所以函数都要返回值
以下三种情况返回值都是None
(1)return None
(2)return
(3)没有return

使用return注意:
(1)当程序执行return,相当于函数执行结束了。
(2)return可以返回多个值,是元组的形式返回,返回值可以使用单个变量获取,
(3)还可以使用跟返回值个数相同的变量分别获取(必须一一对应)
(4)返回值一般采用元组的形式,不采用可变类型作为返回值

def f1(a,b):
    return a+b,a*b
x,y=f1(2,3)
print(x,y)

输出如下:

5 6

五、函数值传递

python分为可变类型值传递和不可变类型值传递

def myfun(a):
    a=a+2
    print("不可变值传递:在myfun的函数中a=",a)

a=3
myfun(a)
print("不可变值传递:在主程序中a=",a)


# 可变类型的值传递
def myfun(a):
    a[0]="new"
    print("可变值传递:在myfun函数中a",a)
a=[1,2,3,4]
myfun(a)
print("可变值传递:在主程序中a=",a)

输出:

不可变值传递:在myfun的函数中a= 5
不可变值传递:在主程序中a= 3
可变值传递:在myfun函数中a ['new', 2, 3, 4]
可变值传递:在主程序中a= ['new', 2, 3, 4]

图解如下:
在这里插入图片描述

六、命名空间和作用域

命名空间:可以保存名字的容器,名字包括:变量名、函数名、类名…
在计算机中,在命名空间中存储名字是以 键–值 存储,以字典结构存储
键—变量名
值—引用内存的对象地址

对于函数而言:
第一次def 就决定了,存在在哪个命名空间中
对于变量而言:
第一次赋值的时候,决定了在哪个命名空间中

每一种命名空间都有自己的区域,当定义的函数、变量或者其他的命名定义到区域内之后,就意味着是对应的命名空间。
1)命名空间的分类
a)内建命名空间:在python解释器启动的时候,就会创建的命名空间,python解释器关闭时销毁
如:内建函数print sum
b)全局命名空间:在读取模块定义的时候创建,当程序运行结束的时候 ,全局命名空间销毁。
如: 全局变量,顶层函数
c)局部命名空间:在函数创建的时候,被创建,当函数执行结束之后,销毁。
如:局部变量,函数的参数,嵌套函数…

需要注意的是:命名空间千万不要理解成包含的关系。区别只是在于作用域不同。

2)作用域
作用域:命名空间中的名字起作用的范围。
a)内建命名空间:所有模块。作用域最大。
b)全局命名空间:在整个py文件中。如果需要使用其他全局命名空间的内容,需要import
c)局部命名空间:只在当前函数内部有效。

需要注意的是:作用域也不是包含关系。只是作用范围的大小。

3)访问原则

a)LEGB原则
L–Local— 局部作用域
E–Enclosing—外围作用域
G–global---- 全局作用域
B–buildin— 内建作用域

① python先到局部命名空间中查找x,如果没有具体的变量或者函数,才去下一个命名空间中查找
如果找到了,则停止查找
② 如果在局部命名空间中找不到,到外围命名空间中查找,如果找到了,则停止,否则去下一个命名空间
③ 如果在外围命名 空间中没找到,则到全局命名空间中查找,如果找到,则停止,否则去下一个命名空间
④ 如果在全面命名空间中没找到,则到内建命名空间中查找
⑤ 如果内建命名空间中仍然没有找到,则报nameError异常

b) 对名称的访问
读取:完全按照LEGB原则
修改:只从当前最小的作用域查找,如果找到,则修改,找不到,
不会继续向上找,而是在当前的作用域中新建
如果必须要修改,可以通过global nonlocal
删除:无论如何也只能删除当前空间的名字

c)global nonlocal

在局部命名空间中,修改全面命名空间的内容 global

i = 2
def fun1():
    global i
    i=1
fun1()
print(i)

输出:

1

在局部命名空间中,修改外围命名空间中的变量 nonlocal

def fun1():
    i=1
    print("外层函数i={}".format(i))
    def fun2():
        nonlocal i
        i = 2
        print("内层函数i={}".format(i))
    fun2()
    print("最终输出i={}".format(i))
fun1()

输出:

外层函数i=1
内层函数i=2
最终输出i=2

d)locals() 、globals():
locals() :找到当前命名空间的名字
globals():全局命名空间中的名字

print(locals())
print(globals())

e)练习

info="address:"
def fun1(country):
    def fun2(area):
        city="北京"
        print(info+country+city+area)
    city="上海"
    fun2("海淀")
fun1("中国")

输出:

address:中国北京海淀

七、lambda()表达式

1、头等函数: 变量和函数名一样,也可以像变量名一样,可以被赋值、被传递、可以作为返回值

a)函数被赋值

def print_world():
    print("hello world!")
print_new = print_world
print_new()

输出:

hello world!

b)函数被传递

def fun1(k):
    print("执行fun1")
    k()
def fun2():
    print("执行fun2")
fun1(fun2)

输出:

执行fun1
执行fun2

2、lambda()函数
lambda()函数格式:lambda 参数:返回式
以下两种功能一致

def add_1(k):
    return k+1
print(add_1(2))

x = lambda k:k+1
print(x(2))

输出:

3
3

lambda 是在函数体比较简单的时候,适合使用,可以作为key时使用,如下:

s=lambda k:k+1
print(s(-20))
li=[1,2,5]
li.sort(key=s)
li.sort(key=lambda k:k+1)
print(li)

八、函数的递归

函数的递归即指函数调用自己本身,有直接递归和间接递归两种
直接递归:A–A
间接递归:A—B---A
递归一定要有条件,来终止递归,否则递归没有意义
递推 回归

递推和循环相比:循环的思路比较复杂,递归思路简单,执行时间比循环长。
1、递归实现
如实现100以内的数进行相加:
a)循环实现

s= 0
for i in range(1,101):
    s+=i
print(s)

输出:

5050

b)递归实现

sum =0
def a(i):
    if i ==1:
        sum=1
    else:
        sum=a(i-1)+i
    return sum
print(a(100))

输出:

5050

递归练习:计算裴波那契数列中第n个数的数值

def fun1(n):
    if n == 1 or n == 2:
        return 1
    else:
        return fun1(n-1)+fun1(n-2)
print(fun1(6))

输出:

8

2、递归深度
递归不仅仅包含自己调用自己,还包含调用其他函数
查看递归深度限制

import sys
print(sys.getrecursionlimit())

输出:

1000

默认是1000
如需修改的话,可采用如下格式

sys.setrecursionlimit(10)

九、函数的注释和文档

函数格式为:

def fun():
    """
    功能介绍
    :return: 返回值的 介绍
    """
    pass

如需查看函数的注释部分,可采用如下语句:

print(fun.__doc__)
help(fun)

另外一种注释方式如下:

def fun(a:int,b:int)->int:
    """
    功能介绍
    :return: 返回值的 介绍
    """
    pass
print(fun.__annotations__)

输出:

{'a': <class 'int'>, 'b': <class 'int'>, 'return': <class 'int'>}

表示参数a的注释是int,b参数的注释是int,函数返回值return为int。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值