本节将进入函数的介绍,函数是Python基础中最精彩的部分之一,接下来将对函数做详细介绍。
函数
函数就是对代码进行一个封装。把实现某一功能的代码进行封装到一起。下次需要使用时不需要进行编写代码直接调用即可
好处:
增加代码的重复性,增加代码的可读性,减少代码的编写量,降低维护成本
函数可以看成解决某类问题的工具
1.函数的定义:
def <name>(arg1,arg2,...,argN): <statements>
def的首行定义了函数名,赋值给了函数对象,并在括号中包含了0个或以上的参数。在调用的时候,在首行的参数名赋值给了括号中的传递来的对象。函数的主体往往都包含了return语句。
def <name>(arg1,arg2,...,argN): <statements> return <value>
Python的return语句可以在函数主体中的任何地方出现。它表示函数调用的结束,并将结果返回至函数调用处。return语句包含一个对象表达式,这个对象给出的函数的结果。return语句是可选的,如果没有出现,则函数自动返回None
#例子 def Diedai(iter): for i in iter: print(i)
函数的定义命名:驼峰规则.
2.传递参数
参数传递的一些简要的关键点
参数的传递是通过自顶将对象赋值给本地变量名来实现的。函数参数(调用者发送的可能的共享对象引用值)在实际中只是Python赋值的另一个实例而已。因为引用是以指针的形式实现的,所有的参数实际上都是通过指针进行传递的。
在函数内部的参数的赋值不会影响调用者。在函数运行时,在函数头部的参数名是一个新的、本地的变量名,这个变量名是函数的本地作用域内的。函数参数名和调用者作用域的变量名是没有别名的
改变函数的可变对象参数的值也许会对调用者有影响。因为参数是简单地复制传入的对象,函数能够就地传入可变对象,因此其结果会影响调用者。可变参数对于函数来说是可以做输入和输出的
***不可变参数是通过值进行传递的
***可变对象是通过‘指针’进行传递的参数的共享与引用:
def f_1(a): return(a) >>> b=90 >>> f_1(b) 90
在调用函数f_1(b)的时候,变量a赋值了对象90,但是,a只是存在于调用的函数之中,在函数中修改a对调用函数的地方没有任何影响,它直接把本地变量a重置为一个完全不同的对象。
没有名称冲突指的是对函数中的一个参数名的赋值(例如a)不会影响到函数作用域中的b的一个变量
def changer(a,b): a=2 b[0]='Python' return(a,b) >>> X=1 >>> L=[1,2] >>> changer(X,L) (2, ['Python', 2]) >>> X,L (1, ['Python', 2])
changer函数给参数a赋值,并给参数b所引用的一个对象赋值。
因为a是在函数作用域的本地变量名,第一个赋值对函数调用者没有影响,它仅仅把本地变量a修改为一个完全不同的对象,并没有改变调用者作用域中的名称X的绑定。
b也是一个本地变量名,但是它被传给了一个可变对象(在调用者作用域中叫做L的列表)。因为第二个赋值是在原处发生变化的对象改变,对函数中b[0]进行赋值的结果会在函数返回后影响L的值
这里:X与a共享一个对象,L与b共享一个对象
参数类型:
1.不传参数
def Fun(): print('不能传参数')
2.必备参数
默认参数 不传参数时默认参数为2,若传参数则默认参数被覆盖
def fun3(b=2): print('默认参数:',b) #可传可不传
可选参数
def fun4(*arg): print('可传0个或多个:',arg) # 可传0个或多个,包装成元组 #fun(*[1,2]) 加个*,就把里面的元素解包,转化为元组。注意:若是字典只取键
关键字参数
def fun5(a,b): # 定义的时候是跟必备参数一样的 print(a,b) # 必须放到最后,通过函数名进行匹配
def fun6(**kwarg): print('关键字参数',kwarg) #包装成字典 #fun6(a=1,b=2) 遵守变量命名规则 #fun6(**{'a':1}) 关键字必须是字符串
参数混合的时候 #关键字参数放到最后 根据定义的顺序 确保必备参数能拿到值且只能拿到一个
def fun7(a,b=1): #必备参数+默认参数 print(a,b) #默认参数必须放在必备参数的后面 def fun8(b,m=1,*a): print(b) print(m) print(a) def fun9(*a,b,m): print(a,b,m)
注意:
在函数的调用中,简单的通过变量名位置进行匹配,但是使用name=value的形式告诉Python依照变量名进行匹配,这些叫做关键字参数。在调用中使用*arg或者**kwarg允许我们在一个序列或字典中相应的封装任意多的位置相关或关键字的对象,并且在它们传递给函数的时候,将它们解包为分开的、单个的参数。
在简单的变量名是通过位置或变量名进行匹配的(取决于调用者是如何进行传参的),但是name=value的形式定义默认了参数值。*name的形式收集了任意的额外的 不匹配的参数到元组中,**name的形式将会收集额外的关键字参数到字典中。
关键字参数必须放到最后
三.内置函数
查看内置函数:dir(__builtins__)
min: min([1,2,3])
max: max([1,2,3])
sum: sum([1,2,3],2) # 表示从2开始加
bin() : 转化为二进制
oct(): 转化为八进制
hex(): 转化为十六进制
ord(‘a’) #97: 转化为对应的ascII码
chr(97) #'a'
补充:
enumerate([1,2,3]) list(enumerate([1,2,3])): #返回一个枚举对象 >>> dict(enumerate([1,2,3,4])) {0: 1, 1: 2, 2: 3, 3: 4} >>> list(enumerate([1,2,3,4],2)) [(2, 1), (3, 2), (4, 3), (5, 4)]
filter(function or None, iterable) --> filter object :
过滤器,第一个参数是一个函数相当于过滤体;
第二个参数是过滤的对象,可迭代对象
>>> list(filter(lambda x:x>1,[1,2,3])) [2, 3]
map(func, *iterables) --> map object 加工
>>> list(map(lambda x:x>2,[2,3,4,5])) [False, True, True, True]
zip(iter1 [,iter2 [...]]) --> zip object 配对
>>> list(zip(([1,2,3]),['a','b','c'])) [(1, 'a'), (2, 'b'), (3, 'c')]
四.作用域
当我们在一个程序中使用变量名时,Python创建、改变或查找变量名都是在所谓的命名空间中I(一个保存变量名的地方)进行的。当我们谈论到搜索变量名对应代码的值的时候,作用域这个术语指的就是命名空间。即代码中变量名被赋值的位置决定了这个变量名能被访问到的范围
作用域需要注意的几点:
一个在def内定义的变量名能够被def内的代码使用,不能在函数外部引用这样的变量名
def之中的变量名与def定义之外的变量名并不冲突,即使是在别处使用相同的变量名。一个在def之外被赋值的变量X与在这个def之中赋值的变量X是完全不同的变量。
作用域的类型:
局部变量:变量在函数内部定义,变量的作用域在函数内部
全局变量:变量在函数外部定义,变量的作用域是全局
global: 用来在函数或其他局部作用域中,声明全局变量(作用于全局)
nonlocal:用来在函数或其他作用域中,声明外层(非全局)变量(作用于局部)
使用global变量的情况:
全局变量可以在函数内部访问,但是不能直接改变
如果在函数内部想要修改局部变量,可以用global来修饰变量
局部变量只能在局部进行访问和修改
如果在函数外部想访问局部变量,也可以用global,将局部变量生命为全局变量
使用nonlocal的情况:
当里层局部,需要修改外层局部时,需要用nonlocal声明.(如嵌套函数)
x=1 # 全局变量 def fun(): y=2 # y是局部类变量,外部不能用 # x+=1 函数内部不能做直接修改全局变量 global x #若要修改,需要授权 x+=1 global z z=3 print(x,y,z) def f1(): global x x+=1 print(x) ##练习 def f2(): q=1 #局部变量,外层 print('局部外层',q) def f3(): w=33 #局部变量,里层 nonlocal q q+=1 print(q) print('局部里层',w) f3()
作用域法则 :
内嵌的模块是全局作用域
全局作用域的作用范围仅限于单个文件
每次对函数的调用都创建了一个新的本地作用域
赋值的变量名除非声明为全局变量或非本地变量,否则均为本地变量