小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
从小到大你们的亲人要你天天向上,努力学习,说的最多的最经典的是不是就下面这几句:
“为了能有好成绩呀”——然后呢?
“为了能考上好大学呀”——然后呢?
“为了将来找个好工作呀”——然后呢?
“为了多赚钱呀”——然后呢?
“为了将来嫁个好人家呀”——然后呢?
韦伯 :“人是自我编织的意义之网的动物。”白话点就是:你总得给自己的生活有点儿目标,不是吗?在小时候我也思考过这个问题:你说你让我好好学习天天向上,要我努力的目标和意义是什么?这个答案在每个人心里都不一样,所以既然你想学Python那么这就是一个目标,一个小意义。
进入主题,今天跟大家分享下何使用 Python 函数基础!如有建议欢迎补充、讨论!
1.什么是函数
函数是最基本的一种代码抽象方式,为了实现某种特定的功能而组织的带名字的代码块。那为什么要使用函数呢?
- 避免代码的重复性,即函数的可以重复使用的。
- 保持代码的一致性,易于修改。即当函数被定义好,即可在很多地方为了实现相同或者相似的功能时可调用函数,而当这些功能发生改变时,我们只需修改该函数即可,不必麻烦的去修改每一处实现该功能的代码。
- 扩展性。
其实简单的来说:函数就是一个有着特定功能作用的工具,例如锤子或者扳手等,每当我们需要使用这些工具的时候,我们不需要临时请师傅制作这样一件工具,这样将会十分繁琐且消耗大量的时间,故我们仅需花费一定的代价去借用这些工具即可这些代价我们暂且可以把其称之为参数,而造这些工具的人为了怕别人不知道这些工具的使用,通常会给这些工具贴上使用说明(函数说明),而制作这些工具也会使用许多材料结构即(函数体),当然一件工具会有命名即(函数名),而当我们使用工具完成后会获得一些效果,我们也可以将这些效果称为(返回值)。当然有工具自然会有工具箱即(类)。
2.函数的特性
定义一个函数要使用 def 语句,依次写出 函数名、括号、括号中的参数和冒号:,然后,在缩进块中编写函数说明,函数体,函数的返回值用 return 语句返回。
def 函数名(参数1,参数2,参数3,......):
"""
函数说明
"""
函数体
return 返回值
例如:
#定义一个获取平方值的函数
def calc(x):
"""
:param x: 输入数字
:return: 获取该数字的返回值
"""
res =x**2
return res #获取返回值
res = calc(10)
print(res)
注意:函数体内部的语句在执行时,一旦执行到 return 时,函数就执行完毕,即函数内 return 后面代码将无效,并将结果返回。如果没有 return 语句的函数,很多时候将其称为过程,但其实默认返回 None。
函数的定义通常类似于变量的定义,定义一个函数只给了函数一个名称,指定了函数里包含的参数,和代码块结构。与变量类似,函数的定义也相当于将代码块以一种特定的方式存储在内存中,只有当调用的时候才会执行,而当我们打印函数名时,其实打印的是内存地址;当打印函数名加括号时,则是返回值。例如:
def greater(name):
print("hello world,%s !"%name)
return name
greater("amanda") #调用函数,执行函数
print(greater) #打印函数地址
print(greater("Sunny Chen")) #调用函数,并且获取返回值
#输出为:
#hello world,Sunny Chen !
#<function greater at 0x00000241C37A3E18>
#hello world,Sunny Chen !
#amanda
当调用函数时,需传入指定类型的参数,才会执行函数体中的代码块。(与java类似有基础的学这个东西很简单)
3.函数的定义
3.1 形参和实参
形参即形式参数,函数完成其工作时所需的一项信息。形参不占用内存空间,只有在被调用时才会占用内存空间,调用完了即被释放。
实参即实际参数,调用函数时传给函数的信息。例如:
#形参和实参
def greater(name): #name即为形参
print("hello world,%s !"%name)
return name
greater("Sunny Chen") #Sunny Chen即为实参
上述而言:在调用函数并传入参数即 greater("Sunny Chen"),将实参 "Sunny Chen" 传递给函数 greater(),这个值就被存储在形参 name 中。
3.2 位置参数和关键字参数
位置参数--> 在调用函数时,Python 必须将每个实参都关联到函数定义的一个形参中,最简单的关联方式就是基于实参的顺序,这种方式即为位置实参。
#位置参数,一一对应,不可多也不可少
def introduction(name,age,hobby):
intro ="My name is %s,I'm %d years old,my hobby is %s."%(name,age,hobby)
print(intro)
introduction("Sunny Chen",21,"eating")
关键字参数 --> 传递给函数的是键值对。由于在实参中的将名称和值关联起来,所以在传入时无需考虑顺序。
#关键字参数,无需一一对应,但也不可多不可少
def introduction(name,age,hobby):
intro ="My name is %s,I'm %d years old,my hobby is %s."%(name,age,hobby)
print(intro)
introduction(age=21,name="Sunny Chen",hobby="dancing")
但位置参数和关键字参数混合使用时:
- 位置参数必须写在关键字参数的左边
- 同一个参数不能两次或者多次传值,只能传一次
#位置参数和关键字参数混合使用
def introduction(name,age,hobby):
intro ="My name is %s,I'm %d years old,my hobby is %s."%(name,age,hobby)
print(intro)
introduction("Sunny Chen",age="21",hobby="eating")
#位置参数必须写在关键字参数左边
#introduction(name="Sunny Chen",21,hobby="eating")
#同一个参数传值只能一次
#introduction("Sunny Chen",21,age="eating")
注:在没有参数组的情况下,传入的值必须与形参的数量一致。
3.3 默认参数
定义函数时我们可以给参数传递默认值,当调用函数时没有传递该参数值时使用默认参数值。带默认值的参数称为默认参数,而无默认值的参数为必需参数,调用函数时必需参数必填,默认参数选填。例如:
'''
@name: Sunny Chen
@test: test font
@msg: 这是由Sunny Chen创建.
@param:
@return:
'''
#默认参数
def introduction(name,age,hobby="runing"):
intro ="My name is %s,I'm %d years old,my hobby is %s."%(name,age,hobby)
print(intro)
#当不给默认参数传值时,默认使用默认参数值
introduction("Sunny Chen",21)
#而给默认参数传值时,则覆盖
introduction("Sunny Chen",age=21,hobby="eating")
3.4 参数组
*args -->传入的多余的位置参数,被 args 接收。即当要想传入多于形参的实参,并且以位置参数的方式传入,多于的参数其以元组的形式接收。
'''
@name: Sunny Chen
@test: test font
@msg: 这是由Sunny Chen创建.
@param:
@return:
'''
def calc(x,y,*args):
res =x+y
# print(x,y)
print(x,y,args)#*args-->多于的参数,位置参数传入,以元组的形式接收
return res
calc(1,2,7,8,6)
calc(2,3,{"name":"sunny"}) #即使传入字典,字典当成整体也会以元组的方式被接收
calc(3,4,["sunny","Chen"],"hello world")
calc(3,4,*["sunny","Chen"]) #相当于该列表遍历,逐个添加至列表并且转为元组
calc(3,4,*"sunny")
# 输出为
#1 2 (7, 8, 6)
#2 3 ({'name': 'sunny'},)
#3 4 (['sunny', 'Chen'], 'hello world')
#3 4 ('sunny', 'Chen')
#3 4 ('s', 'u', 'n', 'n', 'y')
**kwargs -->传入的多余的关键字参数,被 kwargs 接收。即要想传入多于形参的实参,并且以关键字参数的形式传入,多于的参数其以字典的形式接收
'''
@name: Sunny Chen
@test: test font
@msg: 这是由Sunny Chen创建.
@param:
@return:
'''
def calc(x,y,**kwargs):
res =x+y
#print(x,y)
print(x,y,kwargs)#**kwargs-->多于的参数,位置参数传入,以字典的形式接收
return res
calc(x=1,y=3,name="sunny")
calc(1,2,**{"name":"Chen"})
#输出为:
#1 3 {'name': 'sunny'}
#1 2 {'name': 'Chen'}
当在函数值定义参数时,同时定义 *args 、**kwargs 时,可以传入任何参数。多余的位置参数,传入函数,以元组的方式被存储在 *args 中;而多余的关键字参数,传入函数,以字典的方式被存储在 **kwargs 中
'''
@name: Sunny Chen
@test: test font
@msg: 这是由Sunny Chen创建.
@param:
@return:
'''
def calc(x,y,*args,**kwargs):
res =x+y
print(x,y,args,kwargs)#*args,**kwargs-->可以传入任何参数
return res
calc(1,23,"222",["Sunny Chen","Mr-Chen"],greater="hello world")
#输出为:
#1 23 ('222', ['Sunny Chen', 'Mr-Chen']) {'greater': 'hello world'}
4.函数的调用
这里我们需要简单的了解以下关于变量的作用域有哪几种?
- local,局部作用域,即函数中定义的变量
- enclosing,嵌套父级函数的局部作用域,即包含此函数的上级的局部作用域,但不是全局的
- global,全局变量,就是模块级别定义的变量
- built-in,系统固定模块里面的变量,比如 int,bytearray 等
搜索变量的优先级顺序依次为:
局部作用域 >外层作用域 >当前模块中的全局作用域 > python内置作用域,也就是 LEGB
通过以下代码或许我们可以更好的理解这四种作用域之间的关系:
'''
@name: Sunny Chen
@test: test font
@msg: 这是由Sunny Chen创建.
@param:
@return:
'''
x =int(1) #python内置作用域---->B
y =2 #当前模块中的全局变量--->G
def outfunc():
outpara = 3 #外层作用域 --->E
def infunc():
inpara =4 #局部作用域 --->L
print(inpara)
infunc()
outfunc()
同时我们需要知道的是,函数中变量的作用域只与函数的声明有关,而与函数的调用位置无关。通过下面这个例子我们可以知晓:
'''
@name: Sunny Chen
@test: test font
@msg: 这是由Sunny Chen创建.
@param:
@return:
'''
#函数名-->函数内存地址;函数名()-->执行函数,并且获取返回值
name ="zhangsan"
def foo():
name ="lisi"
def bar():
name = "wangwu"
def mitt():
print(name)
return mitt
return bar
foo()()()
#相当于---------------
# bar =foo() #执行foo函数,并且获取bar函数内存地址,并且赋值给bar变量
# mitt =bar() #bar内存地址加括号->执行bar函数,并且获取mitt内存地址
# mitt() #mitt内存地址+括号-->执行mitt函数
#输出结果为:wangwu 而不是zhangsan
这个结果为 wangwu 而不是 zhangsan,则说明函数中变量的作用域只有函数的声明有关,而与函数的调用位置无关。否则,输出肯定为-> zhangsan ,因为若简单的调用 mitt() 其变量为 name=”zhangsan“,则打印的为 zhangsan。
5.关于全局变量和局部变量的使用:
在函数外,一段代码最开始所赋值的变量,其可以被多个函数所调用,其作用域为全局作用域,则称为全局变量。 在函数内定义的变量名,只能在该函数内部引用,不能在函数外部引用这个变量名,其作用域为局部作用域,则称为局部变量。例如:
'''
@name: Sunny Chen
@test: test font
@msg: 这是由Sunny Chen创建.
@param:
@return:
'''
name ="Sunny Chen" #全局变量
def foo():
name ="xiaojia"
print(name)
def bar():
name ="mengxia" #局部变量
print(name)
bar()
print(name)
foo()
#输出结果:
# Sunny Chen
# xiaojia
# mengxia
global -->若想在函数内部修改全局变量,则需要使用 global 关键
'''
@name: Sunny Chen
@test: test font
@msg: 这是由Sunny Chen创建.
@param:
@return:
'''
#注:global必须写在全局变量修改前
name ="Sunny Chen" #全局变量
def foo():
name ="xiaojia"
print(name)
def bar():
global name #修改的为全局变量
name ="mengxia" #局部变量
print(name)
bar()
print(name) #函数没有执行前,故打印的还是原局部变量name
foo() #执行函数
print(name) #函数执行后,修改了全局变量为name =mengxia
复制代码
nonlocal -->若子函数内想修改父级函数的变量,则需要使用 nonlocal 键字
'''
@name: Sunny Chen
@test: test font
@msg: 这是由Sunny Chen创建.
@param:
@return:
'''
name ="Sunny Chen" #全局变量
def foo():
name ="xiaojia"
print(name)
def bar():
nonlocal name
name ="mengxia" #局部变量
print(name)
bar()
print(name) #由于函数没有调用前,函数没有执行,故打印的还是Sunny Chen
foo() #函数执行
print(name) #函数执行后,由于nonlocal关键字,修改的仅是父级的变量name
注:无论是 global 还是 nonlocal,都必须写在变量的修改的前面。否则会报错。
到这结束了,感谢你们的观看,如有不足欢迎补充与讨论!感兴趣的可以关注我的公众号:云芸学派【非常感谢你的点赞、收藏、关注、评论,一键三连支持】