1.函數的介紹
為什麼要有函數?因為在平常寫程式碼時,如果沒有函數的話,那麼將會出現很多重複的程式碼,這樣程式碼重用率就比較低。 。 。而這樣的程式碼維護起來也是很困難的,為了解決這些問題,就出現了函數,用來將一些經常出現的程式碼進行封裝,這樣就可以在任何需要調用這段程式碼的地方調用這個函數就行了。
函數的定義:函數是指將一組語句的集合透過一個名字(函數名稱)封裝起來,要想執行這個函數,只需呼叫其函數名稱即可
特性:
程式碼重複使用
保持一致性
可擴展性
#2.函數的建立
在python中函數定義的格式如下:
def 函数名(形参): 函数体内部代码块
函數的呼叫使用函數名稱(實參) 就可以呼叫函數了。
函數名稱的命名規則和變數的命名規則一樣:
函數名稱必須以下劃線或字母開頭,可以包含任意字母、數字或底線的組合。不能使用任何的標點符號;
函數名稱是區分大小寫的。
函數名稱不能是保留字。
形參與實參的區別:
函數在定義的時候,函數名稱後面的括號中可以加入參數,這些參數就叫做形參,形參:顧名思義就是形式參數,只是一個代號。
實參是在呼叫函數的時候函數名後面的括號中的參數,形參和實參需要一一對應起來,否則呼叫函數會報錯。
3.函數參數及傳回值
前面提到函數的形參與實參要一一對應,那麼參數對應有以下幾種:
必須參數
#關鍵字參數
預設參數
1.必須參數:必須參數必須以對應的關係一個一個傳遞進入函數,函數呼叫時傳遞的實參必須和函數定義時的形參一一對應,不能多也不能少,順序也得一致。
舉個栗子:
def f(name,age): print(name,age) f("小明",18)
##關鍵字參數是實參裡面的概念,在呼叫函數的時候宣告某個參數是屬於某個關鍵字的。使用關鍵字參數允許函數呼叫時參數的順序與聲明時不一致,因為 Python 解釋器能夠用參數名稱來匹配參數值。
舉栗子:def f(name,age): print(name,age) f(name="小明",18)
#3.預設參數
##預設參數是在函數宣告的時候,可以給某個參數指定預設值,這樣的參數叫做預設值參數。如果在呼叫函數的時候,預設參數沒有接收到對應的實參,那麼就會將預設值賦值給這個參數。
舉個栗子:
def f(name,age,sex="male"): print(name,age,sex) f(name="小明",18)
#這樣,就會把預設參數male賦值給sex了。
4.不定長參數*args在python裡面,函數在宣告的時候,參數可以使用(*變數名)的方式來接受不確定長度的參數,但是在python裡面大家約定俗成使用*args接受不定長參數,這樣在呼叫函數的時候傳遞的參數就可以是不定長度的了。 args接受了不定長參數之後,將這些參數放到一個tuple裡面,可以透過存取args來取得這些不定長參數。
舉個栗子:
def f(*args): print(args) f("小明",18,"male")
#列印出來的是一個tuple,裡面存放了(“小明”,18,”male”)這三個元素。
5.不定長參數**kwargs但是上面的args只能接收未命名的參數,那麼假如有類似關鍵字參數的不定長參數怎麼辦呢? python裡面使用(**變數名)來接收不定長的命名變數參數。同樣,python裡面也約定俗成使用**kwargs接收不定長命名參數。 kwargs接收了不定長參數之後,將這些參數放到一個字典裡面,可以透過key取得到對應的參數值。 舉個栗子:
def f(**kwargs): print(kwargs) f(name="小明",age=18,sex="male")
假如一個函數使用了上面所有種類的參數,那該怎麼辦?為了不產生歧義,python裡面規定了假如有多種參數混合的情況下,遵循如下的順序使用規則:
### def f(必須參數,預設參數,*args,**kwargs):# ## pass###如果同時存在args和kwargs的話,args在左邊######預設參數在必須參數的右邊,在*args的左邊#######關鍵字參數的位置不固定( ps:關鍵字參數也不在函數定義的時候確定)###那么,假如有一个列表想要传递进入一个不定长的未命名参数的函数中去,可以在该列表前面加上*实现,同理如果想传递一个字典进入不定长命名参数的函数中去,可以在该字典前面加上**
举个栗子:
def f(*args,**kwargs): print(args) for i in kwargs: print("%s:%s"%(i,kwargs[i])) f(*[1,2,3],**{"a":1,"b":2})
函数的返回值
要想获取函数的执行结果,就可以用return语句把结果返回
注意:
函数在执行过程中只要遇到return语句,就会停止执行并返回结果,也可以理解为 return 语句代表着函数的结束 如果未在函数中指定return,那这个函数的返回值为None
return多个对象,解释器会把这多个对象组装成一个元组作为一个一个整体结果输出。
4.LEGB作用域
python中的作用域分4种情况:
L:local,局部作用域,即函数中定义的变量;
E:enclosing,嵌套的父级函数的局部作用域,即包含此函数的上级函数的局部作用域,但不是全局的;
G:globa,全局变量,就是模块级别定义的变量;
B:built-in,系统固定模块里面的变量,比如int, bytearray等。 搜索变量的优先级顺序依次是:作用域局部>外层作用域>当前模块中的全局>python内置作用域,也就是LEGB。
local和enclosing是相对的,enclosing变量相对上层来说也是local。
在Python中,只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块(如if、try、for等)不会引入新的作用域。
变量的修改(错误修改,面试题里经常出):
x=6 def f2(): print(x) x=5 f2() # 错误的原因在于print(x)时,解释器会在局部作用域找,会找到x=5(函数已经加载到内存),但x使用在声明前了,所以报错: # local variable 'x' referenced before assignment.如何证明找到了x=5呢?简单:注释掉x=5,x=6 # 报错为:name 'x' is not defined #同理 x=6 def f2(): x+=1 #local variable 'x' referenced before assignment. f2()
global关键字
当内部作用域想修改外部作用域的变量时,就要用到global和nonlocal关键字了,当修改的变量是在全局作用域(global作用域)上的,就要使用global先声明一下,代码如下:
count = 10 def outer(): global count print(count) count = 100 print(count) outer()
nonlocal关键字
global关键字声明的变量必须在全局作用域上,不能嵌套作用域上,当要修改嵌套作用域(enclosing作用域,外层非全局作用域)中的变量怎么办呢,这时就需要nonlocal关键字了
def outer(): count = 10 def inner(): nonlocal count count = 20 print(count) inner() print(count) outer()
小结
变量查找顺序:LEGB,作用域局部>外层作用域>当前模块中的全局>python内置作用域;
只有模块、类、及函数才能引入新作用域;
对于一个变量,内部作用域先声明就会覆盖外部变量,不声明直接使用,就会使用外部作用域的变量;
内部作用域要修改外部作用域变量的值时,全局变量要使用global关键字,嵌套作用域变量要使用nonlocal关键字。nonlocal是python3新增的关键字,有了这个 关键字,就能完美的实现闭包了。
5.特殊函数
递归函数定义:递归函数就是在函数内部调用自己
有时候解决某些问题的时候,逻辑比较复杂,这时候可以考虑使用递归,因为使用递归函数的话,逻辑比较清晰,可以解决一些比较复杂的问题。但是递归函数存在一个问题就是假如递归调用自己的次数比较多的话,将会使得计算速度变得很慢,而且在python中默认的递归调用深度是1000层,超过这个层数将会导致“爆栈”。。。所以,在可以不用递归的时候建议尽量不要使用递归。
举个栗子:
def factorial(n): # 使用循环实现求和 Sum=1 for i in range(2,n+1): Sum*=i return Sum print(factorial(7)) def recursive_factorial(n): # 使用递归实现求和 return (2 if n==2 else n*recursive_factorial(n-1)) print(recursive_factorial(7)) def feibo(n): # 使用递归实现菲波那切数列 if n==0 or n==1:return n else:return feibo(n-1)+feibo(n-2) print(feibo(8)) def feibo2(n): # 使用循环实现菲波那切数列 before,after=0,1 for i in range(n): before,after=after,before+after return before print(feibo2(300))
递归函数的优点:定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。
递归特性:
必须有一个明确的结束条件
每次进入更深一层递归时,问题规模相比上次递归都应有所减少
递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返 回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。)
6.函数式编程
关于函数式编程,我理解的也不是很深,但是python中有4个比较重要的内置函数,组合起来使用有时候能大大提高编程效率。
1 filter(function, sequence) str = ['a', 'b','c', 'd'] def fun1(s): if s != 'a': return s ret = filter(fun1, str) print(list(ret))# ret是一个迭代器对象
对sequence中的item依次执行function(item),将执行结果为True的item做成一个filter object的迭代器返回。可以看作是过滤函数。
2 map(function, sequence)
str = [1, 2,'a', 'b'] def fun2(s): return s + "alvin" ret = map(fun2, str) print(ret) # map object的迭代器 print(list(ret))# ['aalvin', 'balvin', 'calvin', 'dalvin']
对sequence中的item依次执行function(item),将执行结果组成一个map object迭代器返回. map也支持多个sequence,这就要求function也支持相应数量的参数输入:
def add(x,y): return x+y print (list(map(add, range(10), range(10))))##[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
3 reduce(function, sequence, starting_value)
from functools import reduce def add1(x,y): return x + y print (reduce(add1, range(1, 101)))## 4950 (注:1+2+...+99) print (reduce(add1, range(1, 101), 20))## 4970 (注:1+2+...+99+20)
对sequence中的item顺序迭代调用function,如果有starting_value,还可以作为初始值调用.
4 lambda
普通函数与匿名函数的对比:
#普通函数 def add(a,b): return a + b print add(2,3) #匿名函数 add = lambda a,b : a + b print add(2,3) #========输出=========== 5 5
匿名函数的命名规则,用lamdba 关键字标识,冒号(:)左侧表示函数接收的参数(a,b) ,冒号(:)右侧表示函数的返回值(a+b)。
因为lamdba在创建时不需要命名,所以,叫匿名函数
更多python常用函数详解相关文章请关注PHP中文网!