1. 程式人生 > >Python第四課----函數

Python第四課----函數

python

函數

一、函數

由若幹語句組成的語句塊,函數名稱、參數列表構成,它是組織代碼的最小單元。

二、函數的作用

1、對代碼的最基本封裝,按照功能組織一段代碼。
2、目的為了復用,減少冗余代碼。

三、函數分類

1、內建函數:max(),reversed()。
2、庫函數:math.ceil。

四、def語句定義函數

def 函數名(參數列表)
  函數體(代碼塊)
  [return 返回值]
函數名就是標識符,命名要求一樣。
語句塊縮進四個空格。
函數中如果沒有return語句,隱式會返回一個None值。
定義中的參數列表是形式參數,只是一種表達,簡稱形參。
2、調用
函數定義,只是聲明,不會執行,需要調用
加上小括號調用
調用時寫的參數是實際參數,是傳入的值,簡稱實參

五、函數的定義,調用

def add(x,y):
  result = x+y
  return result
out = add(4,5)
print(out)
函數---->add,計算結果通過返回值返回、返回值可以使用變量接收,函數是可調用對象,callable(add)試一下,是通用的

六、函數參數

1、參數調用時,傳入的參數要和定義的個數匹配(可變例外)
2、位置參數:def f(x,y,z) 調用時:f(3,4,5),按照順序傳入實參
3、關鍵參數:def f(x,y,z) 調用時:f(x=3,y=4,z=5),使用形參名字傳入實參的方式,順序無所謂
4、傳參:f(z=None,y=10,x=[1])、f((1,),z=6,x=4.1)、f(y=5,z=6,2)最後這種不行,位置參數必須在關鍵字參數之前傳入

七、參數默認值

1、參數默認值:def f(x=4,y=5):這個時候,x,y都有默認值,默認可以輸出
2、參數非常多的時候,並不需要輸出所有參數,有默認值就可以了

八、可變參數

1、問題:有多個數,累加求和
    def add(nums):----這樣的話,nums需要是一個可叠代對象
2、可變位置參數(一個星號)一個形參可以匹配任意個參數x
    def add(*args):----sum=0,for x in args:----sum+=x
3、可變關鍵字參數(兩個星號)
def add(**kwargs):
  for k,v in kwargs.items( ):
     print(“{}={}”.format(k,v))
add(x=1,y=2)
只能用關鍵字調用,組成的是個字典
4、混合使用參數、可變參數:
    1、def  showconfig(username,password,**kwargs)
  2、def  showconfig(username,*args,**kwargs)
  3、def  showconfig(username,password=“mage”,*args,**kwargs)
  4、def fn(*args,x,y,**kwargs):---->這麽寫,調用必須對唯一關鍵詞x、y進行關鍵字傳參
  5、fn(*,x,y)強制將x,y變成唯一關鍵詞
  6、fn(*agrs,x=5)默認值給了可以直接調用fn()

九:參數解構----實參解構

1、fn(x,y):
       return(x+y)
   fn(*(4,5))/*[4,5]/*{4,5}/fn(*range(1,3))/fn((1,2)[0],[3][0])
2、字典解構fn(**{x:5,y:6})或者fn(*d.keys())或d.valus()
3、def fn(*args):
    print(args)
   fn(*[1,2,3])
4、def fn(*args):
    sum = 0
    for x in args:
      sum += x
    print(sum)

十:返回值

十一:作用域

1、一個標識符的可見範圍,也就是變量,一般說變量的作用域
2、x = 5           x = 5
  def foo():         def foo():
  print(x)             x+=1
   foo()                                                print(x)
可執行,x為全局變量       foo()不可執行,下方的x+=1,實際上是重新定義變量x,但是並不存在
3、全局作用域,global變量,可以管轄下面的函數,但函數內部高度自治,自己管自己局部作用域,local變量,裏面可以使用外部的變量,但本地變量,只能在內部使用,外部不可見
4、嵌套結構
def outer( ):                        def outer( ):
  o = 65                           o = 65
  def inner( ):                         def inner( ):
      print("inner { }".format(o))                o = 97
      print(chr(o))                       print("inner { }".format(o))
      print("outer { }".format(o))                print(chr(o))
  inner( )                             print("outer { }".format(o))
outer( )     打印65,65,A                    inner( )  
外部變量內部可用,但賦值即定義                                 outer( )打印結果65,97,a
x = 5
def foo( ):
   y = x+1
   x = 1
   print(x)
foo( )報錯,賦值即定義,上面x用不上,下面的又沒有定義就被y拿來使用
5、全局變量global
x = 5                                 def foo():
def foo():                            global x
   global  x                          x = 10
   x+=1                              x +=1     
將x聲明為使用外部的全局作用域,外面必須有x的定義               這個x=10,是全局變量,是為外面定義一個變量
6、閉包
自由變量:未在本地作用域定義的變量,例如定義在外層函數作用域的變量
閉包:出現在嵌套函數中,指的是內層函數引用了外層函數的自由變量
def counter():
  c = [0]
  def inc( ):
    c[0] += 1
    return c[0]
  return inc
foo = counter( )
print(foo( ),foo( ))
c = 100
print(foo( ))   每次局部變量應該消失,但[ ]是引用對象,每次都會改變,借用的是引用類型
7、nonlocal關鍵字----關鍵字絕對不能被占用
使用了nonlocal關鍵字,將變量標記為在上級的局部作用域中定義,是上級的局部作用域,而不是全局作用域
函數調用為什麽會循環count+=1???
def  counter():
   count = 0
   def inc():
     nonlocal  count
     count +=1
     return count
   return inc
foo = counter( )
print(foo( ),foo( ))     閉包內的函數,在外面foo調用的時候,保留了count這個變量,而且每次都執行了+1

8、函數默認值的作用域:(foo.__defaults__)使用兩個下劃線+defaults+兩個下劃線
def foo(xyz=[]):
  xyz.append(1)
    print(xyz)
print(foo(),id(foo))
print(foo.__defaults__)
print(foo(),id(foo))
print(foo.__defaults__)    
函數的默認值不會發生變化,缺省值不會改變,是個元組,但元組內的列表發生了改變,這是個引用
def foo(w,u="abc",z=123):
  u = "xyz"
  z = 456
  print(w,u,z)
print(foo.__defaults__)
foo("magedu")
print(foo.__defaults__)   
函數的默認值不會發生改變,重新賦值的展示在函數調用裏
9、默認值的作用域用法:
def foo(xyz=[],u="abc",z=123):
  xyz=xyz[:]
  xyz.append(1)
  print(xyz)
foo()
print(foo.__defaults__)
foo()
print(foo.__defaults__)
foo([10])
print(foo.__defaults__)
foo([10,5])
print(foo.__defaults__)
xyz重新被賦值定義,做了拷貝,原xyz還是不變,默認值始終不變
def foo(xyz=None,u="abc",z=123):
  if xyz is None:
    xyz=[]
    xyz.append(1)
    print(xyz)
foo()
print(foo.__defaults__)
foo()
print(foo.__defaults__)
lst = [10]
foo(lst)
print(lst)
print(foo.__defaults__)
foo([10,5])
print(foo.__defaults__)
使用不可變類型默認值,如果使用none,則創建新列表,如果,傳入一個列表,就修改這個列表,lst發生了變化,但是默認值一直沒有發生變化,始終是None,abc,123的元組
def foo(x=None):
  if x == None:
    x = []
    x.append(1)
    return x
lst = foo()
a = foo(lst)
print(a)
一般這麽寫函數,lst=foo()就會執行一次foo(),結果是[1],a=foo(lst)會再執行一次,結果就是[1,1]

十二、函數銷毀:

del或重新賦值

十三、樹:

1、定義:
  (1)、非線性解構,每個元素可以有多個前驅和後繼(這句話,是前驅0或1,後繼多個)
 (2)、樹是n≥0個元素的集合
  (3)、n = 0,則是空樹,樹的根Root沒有前驅
 (4)、其余元素只能有一個前驅,多個後繼
2、遞歸定義:
  (1)、有且只有一個特殊元素根,其余元素劃分為m個互不相交的集合T1,T2.。。。Tm,每一個集合都是樹,稱為T的子樹Subtree
  (2)、子樹也有自己的根
3、樹的概念                                                              
  (1)、結點,樹中的數據元素,每個元素都是一個結點
 (2)、結點的度degree:結點擁有的子樹的數目,稱為度,記作d(v),B的度是1,C的度是2,D的度是3
 (3)、葉子結點,結點的度為0,則是葉子結點leaf、終端結點,末端結點
 (4)、分支結點,結點度不為0,則是分支結點  ABCDE
 (5)、分支,結點之間的關系,A和B的分支,關系,這條線
 (6)、內部結點,除掉根和葉子結點,中間的結點
 (7)、樹的度:樹內各結點,誰的度數大,樹的度數就是多少,上圖為3
 (8)、孩子(兒子Child)結點,結點的子樹的根節點成為成為該結點的孩子
 (9)、雙親(父Parent)結點:一個結點是它各個子樹的根結點的雙親
 (10)、兄弟結點(sibling):具有相同雙親結點的結點
 (11)、祖先結點:從根結點到該結點所經分支上所有的結點。ABD都是G的祖先
 (12)、子孫結點:結點的所有子樹上的結點都成為該結點的子孫,B的子孫是GDHI
 (13)、結點的層次(Level):根結點為第一層,根的孩子是第二層,以此類推,記作L(v)
 (14)、樹的深度(高度Depth):樹的層次的最大值,上圖深度為4
 (15)、堂兄弟,雙親在同一層的結點
4、樹的概念:
 (1)、有序樹:結點的子樹是有順序的,不能交換
 (2)、無序樹:結點的子樹是無序的,可以交換
 (3)、路徑:樹的k個結點n1,n2,。。。nk,滿足ni是n(i+1)的雙親,成為n1到nk的一條路徑,就是一條線下來的,前一個是後一個的父結點,A-B-D-G
 (4)、路徑長度:路徑上結點數-1
 (5)、森林:m≥0顆不相交的樹的集合 D、E、F,結點的子樹集合就是森林
5、樹的特點:
 (1)、唯一的根
 (2)、子樹不相交
  (3)、除了根之外,每個元素只有一個前驅,0或多個後繼
 (4)、根結點沒有前驅,葉子結點沒有後繼
 (5)、如果vi是vj的雙親,則L(vi)=L(vj)-1,雙親比孩子Level小1
 (6)、堂兄弟的雙親不一定是兄弟

十四:二叉樹

1、每個結點最多兩顆子樹:二叉樹不存在度數大於二的結點
2、二叉樹是有序樹,左子樹,右子樹是有順序的,不能交換
3、即使某個結點只有一棵樹,也要確定它是左子樹還是右子樹
4、二叉樹的五種形態
 (1)、空二叉樹   
 (2)、只有根結點的二叉樹
 (3)、根結點只有左子樹
 (4)、根結點只有右子樹
 (5)、根結點有左子樹和右子樹

十五、斜樹:

1、左斜樹:全是左子樹
2、右斜樹:全是右子樹

十六、滿二叉樹:

1、一顆二叉樹的所有分支結點都存在左子樹和右子樹,並且葉子結點只存在最下面一層,一個都不能少,左右完全對稱
2、同樣深度中,滿二叉樹結點最多
3、k為深度(1≤k≤n),則結點總數為2**k-1

十七:完全二叉樹:

1、若二叉樹的深度為k,則二叉樹的層數從1到k-1層的結點數都達到了最大個數,在第k曾的所有結點都集中在最左邊,這就是完全二叉樹(最後一層,從左到右,不能空)
2、滿二叉樹一定是完全二叉樹,完全二叉樹不一定是滿二叉樹 
3、H在左邊,若E有一個分支結點,則不是,必須D有兩個,E有一個左子樹,才是完全二叉樹

十八、二叉樹性質:

1、在二叉樹的第i層上最多有2**(i-1)個結點
2、深度為k的二叉樹,至多有2**k-1個結點
3、對於任意一顆二叉樹T,如果其終端結點為n0,度數為2的結點為n2,則有n0=n2+1(葉子結點,是度數為2的結點加1)上圖中,葉子結點n0=4(HEFG),n2=3(ABC)
4、葉子結點數-1就等於度數為2的結點數
   證明:n0+n1+n2=n(0葉子,1度數為1,2度數為2):
      n0+n1+n2-1(一棵樹的分支數為n-1(總數減去根結點))
      分支數還等於n0*0+n1*1+n2*2-----2n*2+n1
      2*n2+n1=n0+n1+n2-1---->n2=n0-1
5、高度為k的二叉樹,至少有k個結點(左斜樹)
7、具有n個結點的完全二叉樹的深度為int((log2n+1))n開方+1取整,或者math.ceil(log2(n+1))
8、如果有一顆n個結點的完全二叉樹,可以按照層序編號
 (1)、如果i=1,則結點i是二叉樹的根,無雙親,如果i>1,則其雙親是int(i/2),向下取整。就是子結點的編號整除2得到的就是父結點的編號。父節點如果是i,左孩子結點就是2i,右孩子就是2i+1
 (2)、如果2i>n,則結點無左孩子,即結點i為葉子結點,否則其左孩子結點存在編號為2i
 (3)、如果2i+1>n,則結點i無右孩子,否則右孩子存在編號為2i+1

十九、變量名解析原則LEGB

1、Local----先本地作用域,調用結束消亡
2、Enclosing,嵌套函數的閉包的外部函數的命名空間
3、Global----全局,解釋器退出時消亡
4、Build-in 內置模塊的命名空間,解釋器啟動到退出,就是生命周期,print、open等

二十、函數執行流程:

1、函數執行流程:              全局幀中生成foo1、2、3、main函數對象
def foo1(b,b1=3):              main函數調用
  print("foo1 called",b,b1)      main中查找內建函數print壓棧,將常量字符串壓棧,調用函數,彈出棧頂
def foo2(c):                  main中全局查找函數foo1壓棧,將常量100,101壓棧,調用foo1函數,
  foo3(c)                  創建棧幀,print壓棧,字符串和常量壓棧,調用函數,彈出棧頂,返回值。  
  print("foo3 called",c)            後續全部類似
def foo3(d):
  print("foo3 called",d)
def main():
  print("main called")
  foo1(100,101)
  foo2(200)
  print("main called")
main()

二十一、遞歸Recursion

1、函數直接或間接調用自身,就是遞歸

2、遞歸需要邊界條件

3、當不滿足邊界條件時,遞歸前進,當滿足邊界條件時,遞歸結束。


本文出自 “13277682” 博客,謝絕轉載!

Python第四課----函數