Python3函数
函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段.
函数能提高应用的模块性,和代码的重复利用率。Python提供了许多内建函数,比如print()。我们可以直接调用,要调用一个函数,需要知道函数的名称和参数,可以直接从Python的官方网站查看文档:
https://docs.python.org/3/library/functions.html
也可以在交互式命令通过help(print)查看print函数的帮助信息
但你也可以自己创建函数,这被叫做用户自定义函数。
定义函数
- 你可以定义一个自己想要功能的函数,以下是简单的规则:
# 1. 函数代码块以def关键词开头,后接函数标识符名称和圆括号()
# 2. 任何传入参数和自定义变量必须放在圆括号中间,圆括号之间可以用于定义参数.
# 3. 函数的第一行语句可以选择性地使用文档字符串用于存放函数说明
# 4. 函数内容以冒号起始,并且缩进。
# 5. reture[表达式]结束函数,选择性地返回一个值调用方,不带表达式的return相当于返回None.
# 函数: 以功能(完成一件事)为导向,登录,注册,len,一个函数就是一个功能
# 随调随用
#
# 函数作用
# 1. 减少代码重复性.
# 2. 使代码可读性更好.
语法:
Python定义函数使用def关键字,一般格式如下:
def 函数名 (参数列表):
函数体
# 默认情况下,参数值和参数名称是按函数声明中定义的顺序匹配起来的.
# def 关键词开头,空格之后接函数名称和圆括号(),最后还有一个":"
# def 是固定的,不能变,也就是定义函数的关键字
# 函数名: 函数名只能包含字符串、下划线和数字且不能以数字开头。虽然函数名可以随便起,但我们给函数起名字还是要尽量简短,并且要具有可描述性
Example1
我们使用函数来输出"Hello World!"
def hello():
print('hello world')
hello()
# hello world
更复杂点的应用,函数中带上参数变量
# 求元素多少个,长度,传统方法low
li1 = [1, 2, 3, 4, 5]
count = 0
for i in li1:
count += 1
print(count)
# 函数实现求列表多少个元素
def my_len(l):
count = 0
for i in l:
count += 1
print(count)
my_len(li1)
def area(width, height):
return width * height
def print_welcom(name):
print("Welcome",name)
print_welcom("You-Men")
w = 4
h = 5
print("width = ",w,"height = ",h,"area =",area(w,h))
函数调用
Example2
定义一个函数: 给了函数一个名称,指定了函数包含的参数和代码块结构.
这个函数的基本结构完成以后,你可以通过另一个函数调用执行,也可以直接从Python命令提示符执行.
如下实例调用了printme()函数.
# 当函数遇到函数名+ (),函数才会执行
def printme(str):
print(str)
return
printme("我要调用自定义函数")
函数的返回值
一个函数就是封装一个功能,这个功能一般都会有一个最终结果的,比如你写一个登录函数,最终登录成功与否是不是需要返回你一个结果?还有咱们是不是都用过len这个函数,他是获取一个对象的元素的总个数,最终肯定会返回一个元素个数这样的结果:
那么这个返回值如何设置呢?这就得用到python中的一个关键字:return
def date():
print("拿出手机")
print("打开陌陌")
print('左滑一下')
print('右滑一下')
print("找个漂亮的妹子")
return
print("问她,约不约啊!")
print("ok 走起")
# 总结
# 1. 函数中遇到return,此函数结束,不再继续执行
# 2. return会给函数的执行则返回值
# 如果return后面什么都不写,或者函数中没有return,则返回的结果是None
# 如果return后面写了一个值,返回给调用者这个值
# 如果返回多个值,是以tuple(元组)的形式返回的,调用者可以直接使用元组的
函数参数传递
上面有说道函数结构,函数执行,函数返回值,对函数有一个初步的了解,接下来就是函数的参数.
函数是以功能为导向的,上面我们写的函数里面代码都是写死的,也就是说,函数里面更改很麻烦,所以可以通过传参解决这种写死代码.
在Python中,类型属于对象,变量是没有类型的
a=[1,2,3]
a="YouMen"
以上代码中,[1,2,3] 是 List 类型,"Runoob" 是 String 类型,而变量 a 是没有类型,她仅仅是一个对象的引用(一个指针),可以是指向 List 类型对象,也可以是指向 String 类型对象。
可更改(mutable)与不可更改(immutable)对象
在python中,strings,tuples,和numbers是不可更改的对象,而list,dict等则是可以修改的对象.
-
不可变类型: 变量a=5后再赋值a=10,这里实际是新生成一个int值对象10,再让a指向他,而5被丢弃,不是改变a的值,相当于新生成了a.
-
可变类型: 变量赋值la=[1,2,3,4]后再赋值la[2]5则是将list la的第三个元素值更改,本身la没有动,只是其内部的一部分值被修改了.
Python函数的参数传递
不可变类型: 类似C++的值传递,如整数、字符串、元组,如fun(a),传递的只是a的值,没有影响a对象本身,比如fun(a)内部修改a的值,只是修改另一个赋值的对象,不会影响a本身.
可变类型: 类似C++的引用传递,如,列表,字典。如fun(la),则是将la真正的传过去,修改后fun外部的la也会受到影响.
Python中一切都是对象,严格意义我们不能说值传递还是引用传递,我们应该说不可变对象和传可变对象
Python传不可变对象实例
def ChangeInt(a):
a = 10
b = 2
ChangeInt(b)
print(b)
# 上面实例运行结果为:
# 实例中有int对象2,指向他的变量是b,在传递ChangeInt函数时,按传值的方式复制了变量b,a和b都指向了同一个Int对象,在a=10时,则新生成一个int值对象10,并让a指向他
传可变对象实例
可变对象在函数里修改了参数,那么在调用这个函数的函数里,原始的参数也被改变了,比如
# 可写函数说明
def changeme( mylist ):
"修改传入的列表"
mylist.append([1,2,3,4])
print ("函数内取值: ", mylist)
return
# 调用changeme函数
mylist = [10,20,30]
changeme( mylist )
print ("函数外取值: ", mylist)
# 传入函数的和末尾添加新内容的对象是同一个引用,故输出结果如下:
函数内取值: [10, 20, 30, [1, 2, 3, 4]]
函数外取值: [10, 20, 30, [1, 2, 3, 4]]
参数
以下是调用函数的可使用的正式参数类型:
- 必需参数
- 关键字参数
- 默认参数
- 不定长参数
必需参数
必须参数以正确的顺序传入函数,调用时的数量必须和声明时的一样
调用printme()函数,你必须传入一个参数,不然会出现语法错误
# 可写函数说明
def printme(str):
"打印任何传入的字符串"
print(str)
# return
# 不加参数会报错
printme('Hello')
关键字参数
关键字参数和函数调用关系紧密,函数调用使用关键字参数来确定传入的参数值.
使用关键字参数允许函数调用时的顺序和声明时不一样,因为Python解释器能够用参数名匹配参数值
以下实例在函数printme()调用时使用参数名
# 写一个函数,只接受两个数字,int参数,函数功能实现将较大的数返回
def sum(a,b):
if a > b:
return a
elif a == b:
return "相等"
else:
return b
print(sum(2,2))
# 实参角度
# 1. 位置参数,按照顺序,一一对应
# 2. 关键字参数,一一对应
# 3. 混合参数,位置参数一定要在关键字参数的前面
以下实例演示了函数参数的使用不需要指定顺序
def printfo(name,age):
print("名字",name)
print("年龄",age)
return
printfo(age=10,name='youmen')
# 名字 youmen
# 年龄 10
默认参数
调用函数时,如果没有传递参数,则会使用默认参数,以下实例如果没有传入age参数,则使用默认值
# 设置意义在于普遍经常使用的
# 可写函数说明
def printfo(name,age=15):
"打印任何传入的字符串"
print("名字",name)
print("年龄",age)
return
printfo(age=50,name='YouMen')
print("--------------")
printfo(name='Flying')
# 名字 YouMen
# 年龄 50
--------------
# 名字 Flying
# 年龄 15
不定长参数
你可能需要一个函数能处理比当初声明时更多的参数。这些参数叫做不定长参数,和上述 2 种参数不同,声明时不会命名。基本语法如下:
# 可写函数说明
def printfo(arg1,*vartuple):
"打印任何传入的参数"
print("输出:")
print(arg1)
print(vartuple)
# 调用printfo函数
printfo(30,40,50,60)
# 输出:
# 30
# (40, 50, 60)
还有一种就是参数带两个星号**基本语法如下:
# 可写函数说明
def printfo(arg1,**vardict):
print("输出:")
print(arg1)
print(vardict)
printfo(1234,a=2,b=3)
# 加了两个星号**就会以字典的形式导入
# 输出:
# 1234
# {'a': 2, 'b': 3}
如果是单独出现*后的参数必须用关键字传入 :
# 可写函数说明
def printfo(a,b,*,c):
print("输出:")
print(a)
print(b)
print(c)
printfo(1,2,c=3)
# 如果是printfo(1,2,3)会报错
# 输出:
# 1
# 2
# 3
万能参数
(*args)
# 万能参数
# *args,约定俗称,
# * 函数定义时,*代表聚合,他将所有的位置参数聚合成一个元祖,赋值给args
# 写一个函数: 计算你传入函数的所有数字的和
def func(*args):
count = 0
for i in args:
count += i
return count
print(func(1,2,3,4,5,6,7))
( kwargs)
# **kwargs
# 函数定义时,**将所有的关键字参数聚合到一个字典中,将这个字典赋值给了kwargs
def func(**kwargs):
print(kwargs)
func(name='youmen',age=18,sex='boy')
# {'name': 'youmen', 'age': 18, 'sex': 'boy'}
def func(a,b,*args,sex='男',c,**kwargs):
print(a,b)
print(sex)
print(args)
print(c)
print(kwargs)
func(1,2,3,4,5,6,sex='女',c=60)
# 1 2
# 女
# (3, 4, 5, 6)
# 60
# {}
匿名函数
Python使用lambda来创建匿名函数
所谓匿名,意即不再使用def语句这样标准的形式定义一个函数.
- lamdba只是一个表达式,函数体比def简单很多
- lamdba的主题是一个表达式,而不是一个代码块,仅仅能在lambda表达式封装有限的逻辑进去.
- lambda函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数
- 虽然lambda函数看起来只能写一行,却不能同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率
语法
# lambda函数的语法只包含一个语句,如下:
lambda [arg1 [,arg2,....argn]]:expression
# 如下实例:
# 可写函数说明
sum = lambda arg1,arg2 : arg1 + arg2
# 调用sum函数
print("相加后的值为:",sum(10,20))
print("相加后的值为:",sum(20,20))
相加后的值为: 30
相加后的值为: 40
return语句
return [表达式]语句用于退出函数,选择性地向调用方返回一个表达式。不带参数值的return语句返回None。之前的例子都没有示范如何返回数值,以下实例演示了 return 语句的用法:
# 可写函数说明
def sum(arg1,arg2):
"返回2个参数的和"
total = arg1 + arg2
print("函数内:",total)
return total
# 调用sum函数
total = sum(10,20)
print("函数外:",total)
函数内: 30
函数外: 30
强制位置参数
- Python3.8新更加了一个函数形参语法/用来指明函数形参必须使用指定位置参数,不能使用关键字参数的形式
def f(a, b, /, c, d, *, e, f):
print(a, b, c, d, e, f)
# 以下使用方法是正确的:
f(10, 20, 30, d=40, e=50, f=60)
以下使用方法会发生错误:
f(10, b=20, c=30, d=40, e=50, f=60) # b 不能使用关键字参数的形式
f(10, 20, 30, 40, 50, f=60) # e 必须使用关键字参数的形式
函数名称空间,作用域
在python解释器开始执行之后, 就会在内存中开辟一个空间, 每当遇到一个变量的时候, 就把变量名和值之间的关系记录下来, 但是当遇到函数定义的时候, 解释器只是把函数名读入内存, 表示这个函数存在了, 至于函数内部的变量和逻辑, 解释器是不关心的. 也就是说一开始的时候函数只是加载进来, 仅此而已, 只有当函数被调用和访问的时候, 解释器才会根据函数内部声明的变量来进行开辟变量的内部空间. 随着函数执行完毕, 这些函数内部变量占用的空间也会随着函数执行完毕而被清空.
我们首先回忆一下Python代码运行的时候遇到函数是怎么做的,从Python解释器开始执行之后,就在内存中开辟里一个空间,每当遇到一个变量的时候,就把变量名和值之间对应的关系记录下来,但是当遇到函数定义的时候,解释器只是象征性的将函数名读如内存,表示知道这个函数存在了,至于函数内部的变量和逻辑,解释器根本不关心。
等执行到函数调用的时候,Python解释器会再开辟一块内存来储存这个函数里面的内容,这个时候,才关注函数里面有哪些变量,而函数中的变量回储存在新开辟出来的内存中,函数中的变量只能在函数内部使用,并且会随着函数执行完毕,这块内存中的所有内容也会被清空。
我们给这个‘存放名字与值的关系’的空间起了一个名字-------命名空间。
代码在运行伊始,创建的存储“变量名与值的关系”的空间叫做全局命名空间;
在函数的运行中开辟的临时空间叫做局部命名空间也叫作临时名称空间
在py文件中,存放变量与值关系的一个空间叫做全局名称空间,而当执行一个函数时,内存中会临时开辟一个空间,临时存放函数中的变量与值的关系,这个叫做临时名称空间,或者局部名称空间.
python中还有一个空间叫做内置名称空间: 内置名称空间存放的就是一些内置函数等拿来即用的特殊的变量: input,print,list等等.
# 1. 全局命名空间 ---> 我们直接在py文件中,函数外声明的变量都属于全局命名空间
# 2. 局部命名空间 ---> 在函数中声明的变量会存放在局部命名空间
# 3. 内置命名空间 ---> 存放python解释器为我们提供的名字,list,tuple,str,int这些都是内置命名空间.
加载顺序
所谓的加载顺序,就是这三个空间加载到内存的先后顺序,也就是这三个空间在内存中创建的先后顺序,你想想他们能是同时创建么?肯定不是的,那么谁先谁后呢?我们捋顺一下:在启动python解释器之后,即使没有创建任何的变量或者函数,还是会有一些函数直接可以用的比如abs(-1),max(1,3)等等,在启动Python解释器的时候,就已经导入到内存当中供我们使用,所以肯定是先加载内置名称空间,然后就开始从文件的最上面向下一行一行执行,此时如果遇到了初始化变量,就会创建全局名称空间,将这些对应关系存放进去,然后遇到了函数执行时,在内存中临时开辟一个空间,加载函数中的一些变量等等。所以这三个空间的加载顺序为:内置命名空间(程序运行伊始加载)->全局命名空间(程序运行中:从上到下加载)->局部命名空间(程序运行中:调用时才加载。
取值顺序
取值顺序就是引用一个变量,先从哪一个空间开始引用,这个有一个关键点: 从那个空间开始引用这个变量,我们分别举例说明:
# 如果你在全局名称空间引用一个变量,先从局部名称空间引用,全局名, 如果没有,才会向内置名称空间引用
# sum=666
print(sum)
def func():
# sum = 555
print(sum)
func()
name='flying'
def func():
# name='youmen'
print(name)
func()
# 取值顺序(就近原则)
# 从(局部变量找)局部名称空间 ---> 全局名称空间 ---> 内置名称名称空间
作用域
作用域也就是作用范围, 控制生效范围来看全局作用域和局部作用域
全局作用域: 包含内置命名空间和全局命名空间,在整个文件的任何位置都可以使用(遵循 从上到下逐行执行)
局部作用域: 在函数内部可以使用
作用域命名空间
# 1. 全局作用域: 全局命名空间 + 内置命名空间
# 2. 局部作用域: 局部命名空间
def func1():
print('in fun1')
print(3)
def fun2():
print('in fun2')
print(4)
func1()
print(1)
fun2()
print(2)
# in fun1
# 3
# 1
# in fun2
# 4
# 2
print("#############################")
def fun2():
print(2)
def func3():
print(6)
print(4)
func3()
print(8)
print(8)
fun2()
函数名
func()
# 1. 函数名指向的是函数的内存地址.
# 函数名 + ()就可以执行函数
# 2. 函数名就是一个变量,既然是变量就可以赋值运算
# 3. 函数名可以作为容器数据类型的元素
def fun1():
print("in fun1")
def fun2():
print("in fun2")
def fun3():
print("in fun3")
li1 = [fun1,fun2,fun3]
for i in li1:
i()
# 4. 函数名可以作为函数的参数
def func():
print('in func')
def func1(x): # x = func
print('in func1')
return x
ret = func1(func)
ret()
关键字(Global)
# 当我们程序中遇到局部作用域去改变全局作用域的一些变量的需求,可以使用关键字global:
# global第一个功能: 在局部作用域中可以更改全局作用域的变量.
a = 1
def func():
print(a)
func()
# a = 1
def func():
global a
a += 1
print(a)
func()