普通函函数
# 普通函数
# 1. 函数的定义
定义函数,也就是创建一个函数,可以理解为创建一个具有某些用途的工具。定义函数需要用 def 关键字实现,具体的语法格式如下:
def 函数名(参数1,参数2,参数3.。。。):
函数体
return 返回值
2
3
注:
a、函数命名遵循标识符规则,做到见名知意,小驼峰命名法、
b、参数1,参数2,参数3.....形式参数,不同的参数之间使用逗号隔开,参数的数量没有限制,依据具体的需求决定参数的数量
c、函数体:被封装的功能
d、return:结束函数,将返回值返回给调用者,也可单独使用
e、返回值可为常量、变量、表达式
# 定义个空函数,没有实际意义
def pass_dis():
pass
# 定义一个比较字符串大小的函数
def str_max(str1, str2):
str = str1 if str1 > str2 else str2
return str
# 函数的调用
pass_dis()
print(str_max("2", "2#"))
2
3
4
5
6
7
8
9
10
11
12
# 2. 函数调用
调用函数也就是执行函数。如果把创建的函数理解为一个具有某种用途的工具,那么调用函数就相当于使用该工具。函数的调用:实质就是函数入栈出栈的过程
即:函数的入栈:函数被调用;函数的出栈:函数被调用完毕
注:在函数调用的过程要注意避免出现死循环
函数调用的基本语法格式如下所示:
[返回值] = 函数名([形参值])
其中,函数名即指的是要调用的函数的名称;形参值指的是当初创建函数时要求传入的各个形参的值。如果该函数有返回值,我们可以通过一个变量来接收该值,当然也可以不接受。
# 函数的调用
pass_dis()
print(str_max("2", "2#"))
2
3
# 3. 函数返回值 return
返回值:表示一个函数执行完毕之后得到的结果
注:对于return语句不带参数,则返回一个None
- 遇到return,此函数结束,return后面的东西将不会在执行
- return 返回值
- 如果return什么都不写或者干脆就没写return,返回的结果就是None
- 如果return后面写了一个值,返回给调用者这个值
- 如果return后面写了多个结果,,返回给调用者一个tuple(元祖),调用者可以直接使用解构获取多个变量
def test_return():
print("xxx")
return 'aaa'
def test_none(): # 没有return 默认返回None
print("xxx")
a = test_return()
a1 = test_none()
print(a)
print(a1)
"""
xxx
xxx
aaa
None
"""
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 4. 函数值传递和引用传递(包括形式参数和实际参数的区别)
通常情况下,定义函数时都会选择有参数的函数形式,函数参数的作用是传递数据给函数,令其对接收的数据做具体的操作处理。
# 1. 形参、实参
在使用函数时,经常会用到形式参数(简称“形参”)和实际参数(简称“实参”),二者都叫参数,之间的区别是:
形式参数:在定义函数时,函数名后面括号中的参数就是形式参数,例如:
# 定义函数时,这里的函数参数 obj 就是形式参数 def demo(obj): # 形参 print(obj)
1
2
3实际参数:在调用函数时,函数名后面括号中的参数称为实际参数,也就是函数的调用者给函数的参数。例如:
a = "xxxx" # 调用已经定义好的 demo 函数,此时传入的函数参数 a 就是实际参数 demo(a) # 实参
1
2
3
# 2. 值传递和引用传递
python 中,根据实际参数的类型不同,函数参数的传递方式可分为 2 种,分别为值传递和引用(地址)传递:
- 值传递:适用于实参类型为不可变类型(字符串、数字、元组);
- 引用(地址)传递:适用于实参类型为可变类型(列表,字典);
值传递和引用传递的区别是,函数参数进行值传递后,若形参的值发生改变,不会影响实参的值;而函数参数继续引用传递后,改变形参的值,实参的值也会一同改变。
例如,定义一个名为 demo 的函数,分别为传入一个字符串类型的变量(代表值传递)和列表类型的变量(代表引用传递):
说明:可变对象为引用传递,不可变对象为值传递。
引用传递:传递列表或者字典时,如果改变引用的值,就修改了原始的对象
def check(a): # 因为列表是可变对象 print(a) print(id(a)) a.append([1, 2, 3]) return a a = [3, 4, 5] print(check(a)) print(id(a)) """ [3, 4, 5] 44909288 [3, 4, 5, [1, 2, 3]] 44909288 """
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16值传递:当传递不可变对象时,如果改变引用变量的值,只是创建了不同的对象,原始对象并没有改变
def check1(a): # 因为字符串是不可变对象,所有重新创建了地址 print(a) print(id(a)) a = "i am test" print(id(a)) return a a = "This is aa" print(check1(a)) print(id(a)) """ This is aa 58547040 58547120 i am test 58547040 """
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
分析运行结果不难看出,在执行值传递时,改变形式参数的值,实际参数并不会发生改变;而在进行引用传递时,改变形式参数的值,实际参数也会发生同样的改变。
# 5. 参数
# 1. 位置参数
位置参数,有时也称必备参数,指的是必须按照正确的顺序将实际参数传到函数中,换句话说,调用函数时传入实际参数的数量和位置以及类型都必须和定义函数时保持一致。
实参和形参数量必须一致
在调用函数,指定的实际参数的数量,必须和形式参数的数量一致(传多传少都不行),否则 python 解释器会抛出 TypeError 异常,并提示缺少必要的位置参数。
def girth(width , height):
return 2 * (width + height)
#调用函数时,必须传递 2 个参数,否则会引发错误
print(girth(3)) # TypeError: girth() missing 1 required positional argument: 'height'
print(girth(3,2,4)) # TypeError: girth() takes 2 positional arguments but 3 were given
print(area("C语言中文网",3)) # TypeError: unsupported operand type(s) for /: 'str' and 'int'
2
3
4
5
6
7
# 2. 关键字参数
关键字参数是指使用形式参数的名字来确定输入的参数值。通过此方式指定函数实参时,不再需要与形参的位置完全一致,只要将参数名写正确即可。
def dis_str(str1, str2):
print("str1:", str1)
print("str2:", str2)
# 位置参数
dis_str("xxx", "aaa")
# 关键字参数
dis_str("xxx", str2="aaa")
dis_str(str2="xxx", str1="aaa")
# 位置参数必须放在关键字参数之前,下面代码错误
# dis_str(str1="xxx", "aaa") # SyntaxError: positional argument follows keyword argument
2
3
4
5
6
7
8
9
10
11
# 3. 默认参数
在调用函数时如果不指定某个参数,python 解释器会抛出异常。为了解决这个问题,Python 允许为参数设置默认值,即在定义函数时,直接给形式参数指定一个默认值。
Python 定义带有默认值参数的函数,其语法格式如下:
def 函数名(...,形参名,形参名=默认值):
代码块
2
注意,在使用此格式定义函数时,指定有默认值的形式参数必须在所有没默认值参数的最后,否则会产生语法错误。
# 注意1:默认参数体现在形参列表中
def func3(name="abc", age=18):
print("name:%s age:%d" % (name, age))
# 注意2:使用了默认参数,则可以选择不传参,使用的默认值,如果传参,则相当于给形参重新赋值
func3()
func3("jack", 19)
2
3
4
5
6
7
#语法错误,有默认值的参数必须位于所有没默认值参数的后面
def dis_str(str1="http://c.biancheng.net/python/",str2,str3):
pass
2
3
# 4. 不定长参数(*args、**kwargs)
- *args,是以元组的形式不定长传参
- **wkargs是以字典的形式不定长传参
*args
如果您不知道将传递给您的函数多少个参数,请在函数定义的参数名称前添加 *。
这样,函数将接收一个参数元组,并可以相应地访问各项:
def eat(*args): # 表示接受任意参数
print('我想吃',args)
eat('大米饭','中米饭','小米饭') # 我想吃 ('大米饭', '中米饭', '小米饭')
2
3
动态接收参数的时候要注意: 动态参数必须在位置参数后面
def eat(*args,a,b):
print('我想吃',args,a,b)
eat('大米饭','中米饭','小米饭') # TypeError: eat() missing 2 required keyword-only arguments: 'a' and 'b'
# eat函数在调用的时候发现缺少俩个位置参数没有进行传递
2
3
4
通过上述代码发现一个问题就是,我们明明给了多个参数,为什么还会提示参数未传递呢?
原因就是因为这个在搞鬼 把所有的位置参数都给接受了,所有会报错.我们尝试着把a,b放在的前面试试
def eat(a,b,*args):
print('我想吃',args,a,b)
eat('大米饭','中米饭','小米饭') # 我想吃 ('小米饭',) 大米饭 中米饭
2
3
动态接收参数的时候要注意:动态参数必须在位置参数后面
那默认值参数呢?
def eat(a,b,c='白菜',*args):
print('我想吃',a,b,c,args)
eat('豆腐','粉条','猪肉','大葱') # 我想吃 豆腐 粉条 猪肉 ('大葱',) # 我们定义好的白菜没有生效,被猪肉给覆盖了
2
3
我们发现默认值参数写在动态参数前面,默认值的参数是不会生效的
def eat(a,b,*args,c='白菜'):
print('我想吃',a,b,args,c)
eat('猪肉','粉条','豆腐','大葱') # 我想吃 猪肉 粉条 ('豆腐', '大葱') 白菜 # 这样默认参数就生效了
2
3
这个时候如果你不给出关键字传参,那么你的默认值是永远都生效的
动态参数还可以这样传参:
lst = [1,4,7]
# 方法一
def func(*args):
print(args)
func(lst[0],lst[1],lst[2])
# 方法二
def func(*args):
print(args)
func(*lst)
# 在实参的位置上用*将lst(可迭代对象)按照顺序打散
# 在形参的位置上用*把收到的参数组合成一个元祖
2
3
4
5
6
7
8
9
10
11
12
注意: 形参的顺序: 位置参数 , 动态参数 , 默认参数
**kwargs
您还可以使用 key = value 语法发送参数。参数的顺序无关紧要。
在python中可以动态的位置参数,但是*这种情况只能接收位置参数无法接收关键字参数,在python中使用**来接收动态关键字参数
def func(**kwargs):
print(kwargs)
func(a=1, b=2, c=3) # {'a': 1, 'b': 2, 'c': 3}
2
3
动态关键字参数最后获取的是一个dict字典形式
顺序的问题, 在函数调用的时候, 如果先给出关键字参数, 则整个参数列表会报错.
def func(a,b,c,d):
print(a,b,c,d)
func(1,2,c=3,4)
结果:
File "D:/python_object/path2/test.py", line 806
func(1,2,c=3,4) ^
SyntaxError: positional argument follows keyword argument
2
3
4
5
6
7
关键参数必须要放在位置参数后边,由于实参是这个顺序,所以形参接收的时候也是这个顺序.也就是说位置参数必须在关键字参数前面.动态接收关键字参数也要在后面
最终顺序:
**位置参数 > *args(动态位置参数) > 默认值参数 > kwargs(动态默认参数)
这四种参数可以任意的使用
如果想接收所有的参数:
def func(*args,**kwargs):
print(args,kwargs)
func(1,23,5,a=1,b=6) # (1, 23, 5) {'a': 1, 'b': 6}
2
3
字典也可以进行打散,不过需要**
dic = {'a':1,'b':2}
def func(**kwargs):
print(kwargs)
func(**dic) # {'a': 1, 'b': 2}
2
3
4
# 5. 聚合和打散
# 形参: 聚合
def func(*food): # 聚合, 位置参数
print(food)
lst = ["鸡蛋","煎饼果子","猪蹄","滋滋冒油"]
# func(lst)这是当做一个参数传进去了
# func(lst[0],lst[1],lst[2],lst[3])
# 实参: 打散
func(*lst) #打散:把list, tuple, set, str进行迭代打散
# 聚合成关键字参数
def func(**kwargs):#关键字参数
print(kwargs)
# 打散成关键字参数
dic = {"name":'alex', 'age':'18'}
# func(name=dic["name"],age=dic["age"])
func(**dic)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 1.6 函数提供说明文档
函数的说明文档,本质就是一段字符串,只不过作为说明文档,字符串的放置位置是有讲究的,函数的说明文档通常位于函数内部、所有代码的最前面。
def eat(food,drink):
'''
这里描述这个函数是做什么的.例如这函数eat就是吃
:param food: food这个参数是什么意思
:param drink: drink这个参数是什么意思
:return: 执行完这个函数想要返回给调用者什么东西
'''
print(food,drink)
eat('麻辣烫','肯德基')
2
3
4
5
6
7
8
9
在外部查看函数的注释 函数名.*doc*
print(eat.__doc__) #函数名.__doc__
结果:
这里描述这个函数是做什么的.例如这函数eat就是吃
:param food: food这个参数是什么意思
:param drink: drink这个参数是什么意思
:return: 执行完这个函数想要返回给调用者什么东西
2
3
4
5
6
# 1.7 命名空间(全局变量、局部变量)
在python解释器开始执行之后, 就会在内存中开辟一个空间, 每当遇到一个变量的时候, 就把变量名和值之间的关系记录下来, 但是当遇到函数定义的时候, 解释器只是把函数名读入内存, 表示这个函数存在了, 至于函数内部的变量和逻辑, 解释器是不关心的. 也就是说一开始的时候函数只是加载进来, 仅此而已, 只有当函数被调用和访问的时候, 解释器才会根据函数内部声明的变量来进行开辟变量的内部空间. 随着函数执行完毕, 这些函数内部变量占用的空间也会随着函数执行完毕而被清空.
def fun():
a = 10
print(a)
fun()
print(a) # a不存在了已经..
2
3
4
5
我们给存放名字和值的关系的空间起一个名字叫: 命名空间. 我们的变量在存储的时候就 是存储在这片空间中的.
命名空间分类:
全局命名空间—> 我们直接在py文件中, 函数外声明的变量都属于全局命名空间
局部命名空间—> 在函数中声明的变量会放在局部命名空间
内置命名空间—> 存放python解释器为我们提供的名字, list, tuple, str, int这些都是内置命名空间
加载顺序:
内置命名空间
全局命名空间
局部命名空间(函数被执行的时候)
取值顺序:
局部命名空间
全局命名空间
内置命名空间
a = 10
def func():
a = 20
print(a)
func() # 20
2
3
4
5
作用域: 作用域就是作用范围, 按照生效范围来看分为 全局作用域 和 局部作用域
全局作用域: 包含内置命名空间和全局命名空间. 在整个文件的任何位置都可以使用(遵循 从上到下逐⾏执行).
局部作用域: 在函数内部可以使用.
作⽤域命名空间:
- 全局作⽤用域: 全局命名空间 + 内置命名空间
- 局部作⽤用域: 局部命名空间
我们可以通过globals()函数来查看全局作⽤用域中的内容,也可以通过locals()来查看局部作 ⽤用域中的变量量和函数信息
a = 10
def func():
a = 40
b = 20
print("哈哈")
print(a, b)
print(globals()) # 打印全局作用域中的内容
print(locals()) # 打印局部作用域中的内容
func()
2
3
4
5
6
7
8
9
# 1. 局部变量
在函数内部定义的变量,它的作用域也仅限于函数内部,出了函数就不能使用了,我们将这样的变量称为局部变量(Local Variable)。
要知道,当函数被执行时,Python 会为其分配一块临时的存储空间,所有在函数内部定义的变量,都会存储在这块空间中。而在函数执行完毕后,这块临时存储空间随即会被释放并回收,该空间中存储的变量自然也就无法再被使用。值得一提的是,函数的参数也属于局部变量,只能在函数内部使用
def demo():
add = "xxx"
print("函数内部 add =", add)
demo()
print("函数外部 add =", add)
"""
函数内部 add = xxx
print("函数外部 add =",add)
NameError: name 'add' is not defined
"""
2
3
4
5
6
7
8
9
10
11
12
13
def demo(name,add):
print("函数内部 name =",name)
print("函数内部 add =",add)
demo("Python教程","xxx")
print("函数外部 name =",name) # NameError: name 'name' is not defined
print("函数外部 add =",add) # NameError: name 'add' is not defined
2
3
4
5
6
# 2. 全局变量
在所有函数的外部定义变量,这样的变量称为全局变量(Global Variable)。和局部变量不同,全局变量的默认作用域是整个程序,即全局变量既可以在各个函数的外部使用,也可以在各函数内部使用。
定义全局变量的方式有以下 2 种:
在函数体外定义的变量,一定是全局变量,例如
add = "http://c.biancheng.net/shell/" def text(): print("函数体内访问:",add) text() print('函数体外访问:',add) 函数体内访问: http://c.biancheng.net/shell/ 函数体外访问: http://c.biancheng.net/shell/
1
2
3
4
5
6
7
8在函数体内定义全局变量。即使用 global 关键字对变量进行修饰后,该变量就会变为全局变量。例如:
add = "xxx" def text(): print("函数体内访问:", add) text() print('函数体外访问:', add) """ 函数体内访问: xxx 函数体外访问: xxx """
1
2
3
4
5
6
7
8
9
10
11
12注意,在使用 global 关键字修饰变量名时,不能直接给变量赋初值,否则会引发语法错误。
# 3. 获取指定作用域范围中的变量(globals()、locals()、vars())
在一些特定场景中,我们可能需要获取某个作用域内(全局范围内或者局部范围内)所有的变量,Python 提供了以下 3 种方式:
globals()函数 — 能读,能改
globals() 函数为 Python 的内置函数,它可以返回一个包含全局范围内所有变量的字典,该字典中的每个键值对,键为变量名,值为该变量的值。
# 全局变量
Pyname = "Python教程"
Pyadd = "xxx"
def text():
# 局部变量
Shename = "shell教程"
Sheadd = "xxx"
print(globals())
# { ...... , 'Pyname': 'Python教程', 'Pyadd': 'xxx', ......}
# 注意,globals() 函数返回的字典中,会默认包含有很多变量,这些都是 Python 主程序内置的,读者暂时不用理会它们。
2
3
4
5
6
7
8
9
10
11
12
13
可以看到,通过调用 globals() 函数,我们可以得到一个包含所有全局变量的字典。并且,通过该字典,我们还可以访问指定变量,甚至如果需要,还可以修改它的值。例如,在上面程序的基础上,添加如下语句:
print(globals()['Pyname']) # 修改全局变量
globals()['Pyname'] = "Python入门教程"
print(Pyname)
Python教程
Python入门教程
2
3
4
5
6
locals()函数 — 只能读,不能改
locals() 函数也是 Python 内置函数之一,通过调用该函数,我们可以得到一个包含当前作用域内所有变量的字典。这里所谓的“当前作用域”指的是,在函数内部调用 locals() 函数,会获得包含所有局部变量的字典;而在全局范文内调用 locals() 函数,其功能和 globals() 函数相同。
# 全局变量
Pyname = "Python教程"
Pyadd = "xxx"
def text():
# 局部变量
Shename = "shell教程"
Sheadd = "xxx"
print("函数内部的 locals:", locals())
text()
print("函数外部的 locals:", locals())
# 函数内部的 locals: {'Shename': 'shell教程', 'Sheadd': 'xxx'}
# 函数外部的 locals: {...... , 'Pyname': 'Python教程', 'Pyadd': 'xxx', ...... }
# 当使用 locals() 函数获取所有全局变量时,和 globals() 函数一样,其返回的字典中会默认包含有很多变量,这些都是 Python 主程序内置的,读者暂时不用理会它们。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
注意,当使用 locals() 函数获得所有局部变量组成的字典时,可以向 globals() 函数那样,通过指定键访问对应的变量值,但无法对变量值做修改。例如:
#全局变量
Pyname = "Python教程"
Pyadd = "xxx"
def text():
#局部变量
Shename = "shell教程"
Sheadd= "xxx"
print(locals()['Shename'])
locals()['Shename'] = "shell入门教程"
print(Shename)
text()
shell教程
shell教程 # 显然,locals() 返回的局部变量组成的字典,可以用来访问变量,但无法修改变量的值。
2
3
4
5
6
7
8
9
10
11
12
13
14
vars(object)函数 — 只能读,不能改
vars() 函数也是 Python 内置函数,其功能是返回一个指定 object 对象范围内所有变量组成的字典。如果不传入object 参数,vars() 和 locals() 的作用完全相同。
#全局变量
Pyname = "Python教程"
Pyadd = "http://c.biancheng.net/python/"
class Demo:
name = "Python 教程"
add = "http://c.biancheng.net/python/"
print("有 object:")
print(vars(Demo))
print("无 object:")
print(vars())
有 object:
{...... , 'name': 'Python 教程', 'add': 'http://c.biancheng.net/python/', ......}
无 object:
{...... , 'Pyname': 'Python教程', 'Pyadd': 'http://c.biancheng.net/python/', ...... }
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 1.8 gloabal、nonlocal
# 1. global 宗旨
- 可以不用定义global直接修改全局作用域可变类型的数据(list,dict,str..)
- 可以在局部定义global修改全局作用域不可变类型的数据(str,int,float)
global表示. 不再使用局部作用域中的内容了. 而改用全局作用域中的变量
首先我们写这样一个代码, 首先在全局声明一个变量, 然后再局部调用这个变量, 并改变这 个变量的值
a = 100
def func():
global a # 加了个global表示不再局部创建这个变量了. 而是直接使用全局的a
a = 28
print(a) # 100
func()
print(a) # 28
2
3
4
5
6
7
8
9
在函数内部修改全局的变量,如果全局中不存在就创建一个变量
lst = ["麻花藤", "刘嘉玲", "詹姆斯"]
def func():
lst.append("⻢云")
# 对于可变数据类型可以直接进⾏访问
print(lst) # ['麻花藤', '刘嘉玲', '詹姆斯', '⻢云']
func()
print(lst) # ['麻花藤', '刘嘉玲', '詹姆斯', '⻢云']
2
3
4
5
6
7
8
9
# 2. nonlocal宗旨
nonlocal 只修改上一层变量,如果上一层中没有变量就往上找一层,只会找到函数的最外层,不会找到全局进行修改
a = 10
def func1():
a = 20
def func2():
nonlocal a
a = 30
print(a)
func2()
print(a) #
func1()
# 结果:
# 加了nonlocal
# 30
# 30
# 不加nonlocal
# 30
# 20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21