Python从入门到项目实战————函数

系列文章目录

`
在这里插入图片描述



前言

前面我们已经接触了很多的库函数,比如ord()函数,split()函数,这些是已经存在的函数。但是,有时候为了方便,我们也会自己进行编写函数。可能很多人不知道为什么要编写函数,这是由于,自定义的函数可以多次使用,并且可以使代码功能更加集中。看完下面的文章你就能够理解了。

函数的定义和使用

函数是软件设计中重要的组成部分,也是进行代码结构优化的重要技术手段,开发者可以利用函数完成某些指定的数据处理逻辑。

函数的基本定义

格式:
def 函数名称([参数,参数…]):
函数主题代码
[return 返回值]
对于函数的返回值,开发者根据自己是否需要,自行决定。
示例:

# coding:UTF-8
def get ():
    return "I like Python"
print(get())
return_data=get()  #函数有返回值,故可以用变量或者输出函数来来接收
print(return_data)
print(type(return_data))

在这里插入图片描述
上面的程序简单的实现了函数的定义。但是函数的使用绝对不只如此的简单,它的用法非常灵活,需要根据程序员实际需要来进行选择。
而在c++中除了void函数,其他的函数都必须要返回函数值,(visual studio 2019编译器)。所以在python中函数也会返回值,返回的数据为None。
学习过其他编程语言的同学应该知道,函数的定义一般都是有参数的,而在c语言和c++中指针参数的传递则成了最难的部分,但是在Python中,所有的变量都是以引用的方式传递的,就简单了很多。

函数参数传递

示例:定义带参数的函数

# coding:UTF-8
def echo(title,url):
    return "[ECHO]网站名称:{},主页名称:{}".format(title,url)
print(echo("力扣刷题","www.likou.com"))
参数的传递方式

(1).不设置参数名称:
例:echo(“力扣刷题网站”,“www.likou.com”)
(2).设置参数名称:
例:echo(url=“www.likou.com”,title=“力扣刷题网站”)
通过上面的程序我们可以看出,设置参数可以交换参数的位置,而不设置参数则必须按照参数的顺序来传递参数。所以二者的差距就是参数的顺序是否可以交换。对于一般的成程序员来说,最好采用不设置参数名称好,如果是不想遵循参数的顺序,就可以使用设置参数名称的方式。
在Python中还提供了一种函数参数名称定义方式,在“*”后的参数必须设置参数名称,在此之前的则不需要设置参数名称。
示例:

def print_info(name,age,*,job,home):
    print("姓名:%s,年龄:%d,工作:%s,家庭住址:%s" % (name,age,job,home))
print_info("李华",18,home="chinese",job="软件开发工程师")

在这里插入图片描述
当然我们也可以定义默认参数,就是定义参数时我们给其赋值,如果后面不赋值,则就使用我们定义的默认值,否则输出我们重新赋予的值。

函数中引用数据修改的问题

我们知道Python中提供的所有数据类型均为引用类型,即如果传递到函数中的参数是一个列表数据,而且在函数体内修改了此列表的类容,则原始列表的数据类容会受到影响。
示例:

def change_data(list):
    list.append("hello")
infos=["Python"]
change_data(infos)
print(infos)

运行结果:
['Python', 'hello']

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

可变参数

为了方便开发者对函数的调用,Python还支持可变参数,即可以由用户根据实际的需要动态的向函数传递参数,而所有接收到的参数在函数中都采用元组的形式进行接收,可变参可以使用“*参数名称”的语法进行标注;

def math(cmd,*numbers):
    print("可变参数numbers类型:%s,参数数量:%d" % (type(numbers),len(numbers)))
    sum=0;
    if cmd=="+":
        for num in numbers:
            sum+=num
    elif cmd=="-":
        for num in numbers:
            sum-=num
    return sum
print("数字累加计算:%d" % math("+",1,2,3,4,5,6))
print("数字累减计算:%d" % math("-",3,5,6,7,9))

代码运行结果:
可变参数numbers类型:<class 'tuple'>,参数数量:6
数字累加计算:21
可变参数numbers类型:<class 'tuple'>,参数数量:5
数字累减计算:-30

利用可变参数可以极大地减轻我们的工作,当然,除了可变参数,还有关键字参数,利用“**”定义。即在进行参数传递的时候可以按照key=value的形式定义参数项,也可以根据需要传递多个参数项。

关键字参数
# coding:UTF-8
def print_info(name,**urls):  #定义关键字参数
    print("用户姓名:%s" % name)
    print("喜欢的网站:")
    for key,value in urls.items():
        print("\t\- %s:%s" % (key,value))
print_info("李华",wang="www.wang.com",li="www.li.com")

运行结果:
用户姓名:李华
喜欢的网站:
	\- wang:www.wang.com
	\- li:www.li.com

注意:如果一个函数既需要定义关键字参数又需要定义可变参数,则关键字参数必须放在最后,否则会出现语法错误。
实例:

# coding:UTF-8
def print_info(name,age,*inst,**urls):   #定义复合参数
    print("用户姓名:%s,年龄:%d" % (name,age))
    print("用户兴趣:",end="")
    for item in inst:
        print(item,end=",")
    print("\n喜欢浏览的网站:")
    for key,value in urls.items():
        print("\t|-%s:%s"% (key,value))
print_info("李华",18,"唱歌","看书",sig="www.sign.com",read="www.read.com")

在这里插入图片描述
本程序定义了一个包含有混合参数的函数,并且将关键字参数放在了最后,这样才可以保证函数定义的正确性。复合参数的定义顺序为必选参数,默认参数,可变参数,命名关键字参数和关键字参数。

函数递归调用

函数递归调用使一种特殊的函数调用方式,他的好处就是代码量少,方便,但是时间效率太低,一些简单的问题可以使用递归解决,减轻我们的负担。
在这里插入图片描述
例如,利用递归函数实现1到100的累加和

def sum(num):
    if num==1:
        return num
    else:
        return num+sum(num-1)
print("%d" % (sum(100)))

实例:
计算1!+2!+3!+·········+50!

def sum(num):
    if num==1:
        return num
    else:
        return num+sum(num-1)
print("%d" % (sum(100)))

def sum(num):
    if num==1:
        return 1
    else:
        return factorial(num)+sum(num-1)
def factorial(num):
    if num==1:
        return 1
    else:
        return num*factorial(num-1)
print(sum(50))

运行结果:
31035053229546199656252032972759319953190362094566672920420940313

通过上面的函数我们可以发现,递归函数使用起来十分简单,但是如果要做到完完全全掌握递归的精髓,还需要多次练习,用的多了,才能够熟练的使用递归,否则会造成内存泄露。

函数定义深入

学过C语言或者c++的都知道,在c语言和c++语言中会区分全局变量,局部变量,常量,静态变量。在Python中也有全局变量,局部变量的区分。
变量作用域:在函数中定义的变量只允许本函数使用,称之为局部变量。
而在代码非函数中定义的变量称为全局变量,允许多个函数或代码共同访问。通俗的来讲,变量作用域指的是一个变量可以使用的范围。
Python中为了进行全局变量的标注,提供了global关键字,只需要在变量前利用global进行声明就可以自动将函数中操作的变量设置为全局变量。
在这里插入图片描述

提示:关于变量名称解析的LEGB原则

LEGB原则规定了在一个Python程序中变量名称的查找顺序:
(1).L(local):函数类局部变量名称
(2).E(Enclosing Function Locals):外部嵌套函数变量名称
(3).G(Global):函数所在模块或程序文件的变量名称
(4).B(Builtin):内置谋模块的变量名称

实例:观察全局变量和局部变量

#全局变量与局部变量
nums=100
def change_num():
    num=30        #局部变量
    global nums     #全局变量
    print("局部变量:%d,全局变量:%d" % (num,nums))
change_num()
显示结果:
局部变量:30,全局变量:100

可能很多人不理解全局变量和局部变量何时使用,当我们需要在多个函数进行使用某一个变量的时候,或者需要经过多种关系进行改变的变量就使用全局变量,对于只需要在某一个函数中使用的变量就使用局部变量。

提示:关于参数名称的另一种定义形式

在代码开发中经常会出现同一变量名被重复使用的情况,为避免使用时出现问题,项目开发定义变量时往往使用一些标记结合变量名称进行定义。例如:
\\ 函数局部变量(本地变量,使用local_var_作为前缀):local_var_name.
\\函数参数(使用function_parameter_作为前缀)function_parameter_name
\\函数全局变量(字母大写,使用GLOBAL_VAR_作为前缀):GLOBAL_VAR_name.

实例:使用globals()与locals()函数
在Python中,提供了两个函数可以动态的获取程序文件中的全局变量(globals())与局部变量(locals()),这两个函数会将所有的变量以字典形式返回。

# coding:UTF-8

number=100
def print_var():
    num=30
    info="I like Python"
    print(globals())
    print(locals())
print_var()

在这里插入图片描述
通过系统给定的函数我们可以得出上下文本的全部变量信息。

在函数中我们也会用“”“”“”来作为注释,哪我们又该如何去获取注释的相关信息?
使用"doc”获取:
实例:

# coding:UTF-8
def change_doc():
    """
    在Python中,我们使用的变量一般是局部变量,如果我们要使用全局变量,那么我们就可以用global来进行标注
    """
    print("Hello Python ")
print(change_doc.__doc__)
运行结果:
在Python中,我们使用的变量一般是局部变量,如果我们要使用全局变量,那么我们就可以用global来进行标注
    
闭包

Python允许函数进行嵌套定义,即一个函数的内部可以继续定义其他的函数,将外部函数作为其内部嵌套函数的引用环境,并且在对内部函数进行处理的时候外部函数的引用环境一直都会不变,这种将内部函数与外部函数作为整体处理的函数嵌套结构在程序设计中称为闭包(closure)。
实例:定义函数闭包结构

# coding:UTF-8
def outer_add(n1):
    def inner_add(n2):
        return n1+n2
    return inner_add        #返回内部函数引用;
oa=outer_add(10)  #接受外部函数引用;
print("加法计算结果: %d" % oa(20))      #执行内部函数;
print("加法计算结果: %d" % oa(30))
显示结果:
加法计算结果: 30
加法计算结果: 40

本程序实现了一个闭包操作,在outer_add()函数中嵌套了一个内部函数,在使用外部参数的时候,我们往往要先进行对外部函数引用,这样才能实现对内部函数的调用,于是我们用了oa来接收外部函数的引用,然后继续对oa操作,这时表示的就是对内部函数的使用。

实例:内部函数修改外部函数变量类容
使用闭包结构的最大特点时可以保持外部函数操作的状态,但是如果要想在内部函数中修改外部函数中定义的局部变量或参数的类容,则必须使用nonlocal关键字.
实例:

# coding:UTF-8
def print_data(count):
    def out(data):
        nonlocal count   #修改外部函数变量;
        count+=1
        return "第{}次输出数据:{}".format(count,data)
    return out
oa=print_data(0)
print(oa("www.Python.com"))
print(oa("Python"))
print(oa("good!"))

显示结果:1次输出数据:www.Python.com
第2次输出数据:Python
第3次输出数据:good!
lambda表达式

在Python中所有定义的函数都会提供一个函数名称,实际上提供函数名称就是为了方便函数进行调用,然而在某些情况下,函数可能只调用一次,这样的函数就被称为匿名函数,匿名函数需要lambda关键字进行定义。

# coding:UTF-8
sum=lambda x,y:x+y;  #定义一个匿名函数,实现两个数字相加(x,y);
print(sum(2,3))

通过程序我们可以发现,我们在使用lambda的时候,我们不需要使用return也可以直接返回计算结果。当然也可以不用写函数名称。
实例:结合闭包使用lambda表达式

def add(n1):
    return lambda n2:n2+n1
oper=add(100)
print(oper(30))

我们在使用的lambda表达式的时候一般都是为了程序简单化,减少代码量,也减少了不必要的繁琐。

主函数

学过c语言的同学应该知道每一个程序最后的实现都是在主函数中,但是在python中并没有提供主函数的定义结构,如果有需要,用户可以自己定义,此时就可以基于“name”这个系统变量来实现此类操作。
实例:定义主函数并观察_name_

def main():
    print("自定义程序主函数,表示程序执行的起点!")
    print("更多知识请关注“csdn博主:心随而动”")
if __name__=="__main__":
    main()

内置对象函数
函数 描述
callable(object) 判定给定的对象是否可以调用,如果可以调用,则返回True,否则返回False
eval(object,【,globals[]】) 对给定的对象(可能是字符串或编译对象)进行单行代码计算,可以选择性地址传递全局变量或局部变量信息.
exec(source,filename,mode) 对给定的对象(可能是字符串或编译对象)进行多代码计算,可以选择性的传递全局变量或者局部变量信息(类型必须为字典)
complie(source,filename,mode) 对传入的source数据进行编译,可以通过filename指定所需要编译的代码文件(一般为空),在编译时需要明确指明运行模式,该选项有三种配置:(1).mode=“eval”:使用eval函数执行代码() ,(2).mode=“single”:使用single()函数执行代码,(3).mode=“exec”:使用exec函数执行代码

一.callable()函数
在Python中,对函数或者变量都是引用传递,所以为了判断某些操作结构是否可以被调用,就可以通过callable()函数实现。

# coding:UTF-8
print("input()函数是否可以被调用:%s" % callable(input))
print("Hello字符串是否可以被调用:%s" % (callable("Hello")))
def get_info():
    return "I like Python"
temr_fun=get_info
print("函数get_info是否可以被调用:%s" % (callable(get_info)))
print("函数temr_fun是否可以被调用:%s" % (callable(temr_fun)))

运行结果:
input()函数是否可以被调用:True
Hello字符串是否可以被调用:False
函数get_info是否可以被调用:True
函数temr_fun是否可以被调用:True

二.eval()函数
程序事多个表达式的集合,在Python中开发者可以将要执行的表达式直接定义成字符串的形式,随后使用eval()函数动态的编译和执行,如图:
在这里插入图片描述
例:使用eval()函数动态编译并执行表达式

num=10
result=eval("3*num")    #直接解析字符串定义的程序表达式
print("计算结果为:%d" % (result))

在使用eval()函数执行时,也可以将程序中的 全局变量或局部变量传递到eval()函数执行的表达式中,但是要求这些变量必须以字典的形式传递。
实例:使用全局变量

num=10
result=eval("3*num")    #直接解析字符串定义的程序表达式
print("计算结果为:%d" % (result))
global_num=10
global_str="数据加法计算结果:{}"
var_dict=dict(num=global_num,info=global_str)   #字典数据表示全局变量
result=eval("info.format(num*2)",var_dict)
程序运行结果:
数据加法计算结果:20

在序列的定义中,字符串可以转换为列表,元组,字典,但是要完成此功能需要通过不同的转换函数进行处理,而这一转换功能也可以直接利用eval()函数统一实现。
实例:

list_str="[1,2,3]"         #列表结构字符串;
tuple_str="{1,2,3}"        #元组结构字符串
dict_str="{1:'one',2:'two',3:'three'}"  #字典结构字符串
list_eval=eval(list_str)  #字符串转换为列表
tuple_eval=eval(tuple_str)
dict_eval=eval(dict_str)
print("[list]序列结构:%s,序列类型:%s" % (list_eval,type(list_eval)))
print("[tuple]序列结构:%s,序列类型:%s" % (tuple_eval,type(tuple_eval)))
print("[dict]序列结构:%s,序列类型:%s" % (dict_eval,type(dict_eval)))

在这里插入图片描述

exec()函数

与eval()函数功能类似的还有一个函数,即exec()函数,该函数也可以更具字符串定义的程序表达式动态的编译和运行。
实例:

statement="for item in range(1,10,2):" \
     "print(item,end=',')"
exec(statement)
代码显示结果:
1,3,5,7,9

提示:eval()函数和exec()函数的区别
(1).eval函数只能执行一个简单的表达式,并且可以接受表达式返回的值。
(2).exec函数可以执行多行程序语句,但是不能接受函数的返回值,返回的事None。

compile()函数

eval()和exec()两个函数在执行字符串中表达式的操作时,都采用先编译再执行的模式完成,但是这样的实现方式可能会对程序性能有所影响。为了提高再运行时生成代码对象的速度。Python提供了compile()函数,此函数可以直接在源代码程序编译时就自动对字符串中表达式进行解析,而在程序运行的时候将不再进行动态编译,而实直接执行编译好的对象。
实例:使用eval模式
代码:

statement="100+200+50-30"
code_eval=compile(statement,"","eval")  #使用eval模式
result=eval(code_eval)
print("计算结果:%d" % result)

实例:使用single执行模式

input_data=None
statement="input_data=input('请输入你最喜欢的学校:')"
code_exec=compile(statement,"","single")      #使用single执行模式
exec(code_exec)
print("输入数据为:%s" % (input_data))

本程序通过字符串定义的是一个完整的语句,不再是一个简单的表达式,所以compile()函数中定义的执行模式可以是single或exec,由于只有单行语句,所以使用single模式并用exec函数执行编译对象。
实例:使用exec模式

infos=[]  #保存全部键盘输入数据
statement="for item in range(2):"\
    "infos.append(input('请输入你要输入的数据:'))"
code_eval=compile(statement,"","exec")
exec(code_eval)
exec("print('经常访问的网址是:%s' % infos)")

总结

1.函数是一段可以重复调用的代码段,Python中定义函数统一使用def关键字进行定义,如果需要有返回值,则直接在函数中添加return语句即可实现。
2.Python中定义的函数支持多种参数类型(具体的看前面的解析)
3. 在函数中使用参数时需要注意全局变量与局部变量的概念,全局变量可以利用关键字global定义。
4. 函数允许嵌套定义,嵌套后的函数可以方便的实现外部状态的维护,这样的函数嵌套结构就被称为闭包处理。如果要在函数中对外部函数的变量进行修改,就要用nonlocal关键字。
5. 使用lambda表达式可以定义匿名函数,lambda函数的函数体比较简单。如果定义的函数其函数体中有多行代码,建议定义为有名函数。
6. 一个函数允许调用自己,这样的处理方式就是递归调用。
7. Python中可以使用eval,exec函数动态的解析字符串中提供的表达式或代码,也可以使用compile()函数对要执行的代码提前翻译,以提高程序的执行能力。

猜你喜欢

转载自blog.csdn.net/qq_59931372/article/details/125765059