关于函数的定义这里不再赘述,我比较倾向于想象函数是一个黑盒子,我需要得到某个东西的时候直接进去拿,比如我想了解python函数,那么直接百度搜索python函数,百度搜索就会给我展示python函数的相关内容,这里的百度搜索就是一个函数,我才不管它内部是怎么实现的,当然如果自己写一个函数,你就要考虑很多东西了,下面说说4个你可能不知道的事:
1.函数高逼格调用
函数的本质是一个对象,类似于变量,当声明变量时,会产生一个内存地址来存变量,如下代码:
a=12345b=aprint(‘a的内存地址是:%s\nb的内存地址是:%s‘%(id(a),id(b)))#运行结果:#a的内存地址是:31187568#b的内存地址是:31187568
会发现,在声明变量a时,开放了一个内存地址来存a变量,再声明变量b=a,可以看出a和b的内存地址是一样的,也就是说a和b都是指向这个内存地址的内容。那么,其实函数也是一样的道理,声明函数时开放了一个内存地址来存函数内容,代码如下:
defa():returnb=aprint(‘a的内存地址是:%s\nb的内存地址是:%s‘%(id(a),id(b)))#运行结果:#a的内存地址是:2147864#b的内存地址是:2147864
可以看出函数在声明时,内存的工作原理是一样的,由此可见,在调用函数的时候,可先给函数变个对象,也就是换个名字,如果你觉得在调用函数时,改个高逼格的名字来运行就是我要说的高逼格调用的话,请往下看,我的铁。
defplay():print(‘出去浪‘)defstudy():print(‘好好学习‘)whileTrue:
choice=input(‘请输入你的选择:1代表出去浪;2代表好好学习\n‘)if choice==‘1‘:
play()break
elif choice==‘2‘:
study()break
else:print(‘请输入正确的选择!‘)continue
这时常规的函数调用,当用户输入选择时,调用对应的函数名称,但是这样的话,每次都要来写调用的函数,如果选项很多,在写的时候就比较麻烦,下面是改进后的代码(用字典形式存选项、函数名),调用的时候只需要通过key(choice)调用value(函数名)即可:
defplay():print(‘出去浪‘)defstudy():print(‘好好学习‘)
def_method={‘1‘:play,‘2‘:study}whileTrue:
choice=input(‘请输入你的选择:1代表出去浪;2代表好好学习\n‘)if choice==‘1‘ or choice==‘2‘:
def_method[choice]()break
else:print(‘请输入正确的选择!‘)continue
如上,实现了通过key(choice)调用value(函数名)来满足用户输入选项调用不同函数,可以应用到比如用户登录,判断用户角色调用不同的函数,根据用户的输入选择调用对应的函数。
2.函数变量作用域
Python 中只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块(如 if/elif/else/、try/except、for/while等)是不会引入新的作用域的,也就是说这这些语句内定义的变量,外部也可以访问。但是这里讲的函数是有变量作用域的概念,Python作用域有4种:
L (Local) 局部作用域
E (Enclosing) 闭包函数外的函数中
G (Global) 全局作用域
B (Built-in) 内建作用域
x = int(2.9) #内建作用域
g_count = 0 #全局作用域
defouter():
o_count= 1 #闭包函数外的函数中
definner():
i_count= 2 #局部作用域
找变量的顺序:局部作用域->闭包函数外的函数中->全局作用域->内建作用域,一层层找上去,找到为止。这里举两个例子:
第一个例子,是一个python习题:统计文件夹中总共写了多少行代码,包括空行和注释,要把代码的行数、空行数、注释行数都统计出来
importos
comment_nums=0
space_nums=0
code_nums=0defcount(file,a,b,c):
with open(file,‘r‘,encoding=‘utf-8‘) as f:
arr=f.readlines()for i inarr:if i.lstrip().startswith(‘#‘):
a+=1
elif i==‘\n‘:
b+=1
else:c+=1
returna,b,c
files=os.listdir(‘.‘)for i infiles:if i.endswith(‘.py‘):
comment_nums,space_nums,code_nums=count(i,comment_nums,space_nums,code_nums)print(‘注释有%s行\n空行有%s行\n代码有%s行‘%(comment_nums,space_nums,code_nums))
注意到上述代码中必须要函数返回相应的统计数才可以接受到从而修改全局变量:comment_nums,space_nums,code_nums;这样写有点麻烦,下面引入全局变量修改代码如下:
importos
comment_nums=0
space_nums=0
code_nums=0defcount(file):globalcomment_numsglobalspace_numsglobalcode_nums
with open(file,‘r‘,encoding=‘utf-8‘) as f:
arr=f.readlines()print(arr)for i inarr:if i.lstrip().startswith(‘#‘):
comment_nums+=1
elif i==‘\n‘:
space_nums+=1
else:code_nums+=1
#print(a,b,c)
#return a,b,c,d
count(‘2.日志删除.py‘)print(space_nums,code_nums,comment_nums)
第二个例子,nonlocal关键字的例子,当内部作用域想修改外部作用域的变量时,就要用到global和nonlocal关键字了。
defouter():
num= 10
definner():
nonlocal num#nonlocal关键字声明
num = 100
print(num)
inner()print(num)
outer()#输出结果
100
100
如果要修改嵌套作用域(enclosing 作用域,外层非全局作用域)中的变量则需要 nonlocal 关键字了.
3.lambda匿名函数
lambda可以定义一个匿名函数,而def定义的函数必须有一个名字。
lambda的函数体是一个简单的表达式而不是语句块.
基本用法如下
f = lambda x, y, z: x + y +z
f(2, 3, 4)
等价于:
def func(x, y, z): return x + y + z
4.可变参数
默认参数值:这允许用户为函数的参数定义一些默认值。这种情况下,可以以更少的参数来调用该函数,而函数调用时未提供的参数,Python会使用默认提供的值作为这些参数值。
(1)只提供非缺省位置参数值。在本例中,缺省参数取默认值:
def show_args(arg, def_arg=1, def_arg2=2):return "arg={}, def_arg={}, def_arg2={}".format(arg, def_arg, def_arg2)
show_args("tranquility")#输出:
‘arg=tranquility, def_arg=1, def_arg2=2‘
(2)用提供的值覆盖一些默认的参数值,包括非缺省位置参数:
def show_args(arg, def_arg=1, def_arg2=2):return "arg={}, def_arg={}, def_arg2={}".format(arg, def_arg, def_arg2)
show_args("tranquility", "to Houston")#输出
‘arg=tranquility, def_arg=to Houston, def_arg2=2‘
(3)为所有参数提供值,可以用这些值覆盖默认参数值:
def show_args(arg, def_arg=1, def_arg2=2):return "arg={}, def_arg={}, def_arg2={}".format(arg, def_arg, def_arg2)
show_args("tranquility", "to Houston", "the eagle has landed")#输出
‘arg=tranquility, def_arg=to Houston, def_arg2=the eagle has landed‘
任意的参数列表:Python还支持定义这样的函数,该函数可以接受以元组形式传递的任意数量的参数,Python教程中的一个例子如下所示:
def write_multiple_items(file, separator, *args):
file.write(separator.join(args))
f= open("test.txt", "wb")
write_multiple_items(f," ", "one", "two", "three", "four", "five")#上面的参数one、two、three、four、five捆绑在一起共同组成了一个元组,通过参数args就能访问该元组
关键字参数:以“kwarg=value”的形式使用关键字参数也可以调用函数。其中,kwarg指函数定义中使用的参数名称。以下面定义的含有默认和非默认参数的函数为例:
def show_args(arg, def_arg=1):return "arg={}, def_arg={}".format(arg, def_arg)
show_args(arg="test", def_arg=3)
show_args(test)
show_args(arg="test")
show_args("test", 3)#在函数调用中,关键字参数不得早于非关键字参数,所以以下调用会失败:
show_args(def_arg=4)#函数不能为一个参数提供重复值,所以下面的调用方法是非法的:
show_args("test", arg="testing")
在上面的例子中,参数arg是位置参数,所以值“test”会分配给它。而试图将其再次分配给关键字arg,意味着在尝试多重赋值,而这是非法的。
传递的所有关键字参数必须匹配一个函数接受的参数,而包含非可选参数的关键字顺序并不重要,所以下面调换了参数顺序的写法是合法的:
show_args(def_arg="testing", arg="test")
收工,欢迎大家反馈问题、补充不足。