Python函數

函數基礎

1、定義函數

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
"""
#語法
def 函數名(參數1,參數2,參數3,...):
     '''註釋'''
     函數體
     return 返回的值
 
#函數名要能反映其意義
"""
 
def tol(a,b):              # def 定義函數
     """
     求和
     :param a:
     :param b:
     :return:
     """
     ret = a + b                 #函數體
     return ret             #函數返回
tol( 1 , 2 )                    #調用函數  函數名+括號就是調用
print (tol( 1 , 2 ))            #打印返回值,若是沒return語句,返回值爲None

2、使用原則:先定義,再調用

  • 函數即「變量」,「變量」必須先定義後引用.
  • 未定義而直接引用函數,就至關於在引用一個不存在的變量名.
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 狀況1:
def foo():
     print ( 'from foo' )
     bar()
foo()  # 報錯 NameError: name 'bar' is not defined
 
# 狀況2:
def bar():
     print ( 'from bar' )
def foo():
     print ( 'from foo' )
     bar()
foo()  # 正常
 
# 狀況3:
#######定義階段#######
def foo():
     print ( 'from foo' )
     bar()
def bar():
     print ( 'from bar' )
#######定義階段#######
 
#######調用階段#######
foo()  #正常
# ******不報錯,調用的時候已經定義好了******#先定義,後調用
#######調用階段#######

3、定義函數的三種形式

?
1
2
3
4
5
6
7
8
def tell_tag(tag,n): #有參函數
     print (tag * n)
 
def tell_msg(): #無參函數
     print ( 'hello world' )
 
def tell_blank(): #空函數
     pass

4、調用函數的三種形式

  • 語句形式:foo()
  • 表達式形式:3*len('hello')
  • 當中另一個函數的參數:range(len('hello'))

5、函數返回值

  • 無return->None
  • return 1個值->返回1個值
  • return 逗號分隔多個值->元組

6、函數參數

形參即變量名就是函數定義階段的參數,實參即變量值就是函數調用階段的參數,函數調用時,將值綁定到變量名上,函數調用結束,解除綁定python

  1. 位置參數:按照從左到右的順序定義的參數
    • 位置形參:必選參數
    • 位置實參:按照位置給形參傳值
  2. 關鍵字參數:按照key=value的形式定義的實參
    • 無需按照位置爲形參傳值
    • 注意1:關鍵字實參必須在位置實參右面
    • 注意2:對同一個形參不能重複傳值
  3. 默認參數:形參在定義時就已經爲其賦值
    • 能夠傳值也能夠不傳值,常常須要變得參數定義成位置形參,變化較小的參數定義成默認參數(形參)
    • 注意1:只在定義時賦值一次
    • 注意2:默認參數的定義應該在位置形參右面
    • 注意3: 默認參數一般應該定義成不可變類型
  4. 可變長參數:
    • 可變長指的是實參值的個數不固定
    • 而實參有按位置和按關鍵字兩種形式定義,針對這兩種形式的可變長,形參對應有兩種解決方案來完整地存放它們,分別是*args,**kwargs
  5. 命名關鍵字參數:*後定義的參數,必須被傳值(有默認值的除外),且必須按照關鍵字實參的形式傳遞
    • 能夠保證,傳入的參數中必定包含某些關鍵字
#===========*args===========
def foo(x, y, *args):
    print(x,'-',y,'-',*args,'-',args)

foo(1, 2, 3, 4, 5)  #1 - 2 - 3 4 5 - (3, 4, 5)
foo(1, 2,[3, 4, 5]) #1 - 2 - [3, 4, 5] - ([3, 4, 5],)
foo(1, 2, *[3, 4, 5]) #1 - 2 - 3 4 5 - (3, 4, 5)
foo(*[1, 2, 3]) #1 - 2 - 3 - (3,)

print(*[3, 4, 5]) # 3 4 5
print(*{'b': 2, 'a': 1, 'c': 3}) # b c a

#===========**kwargs===========
def foo(x, y, **kwargs):
    print(x,'-',y,'-',*kwargs,'-',kwargs)

foo(1, y=2, a=1, b=2, c=3) #1 - 2 - b c a - {'b': 2, 'c': 3, 'a': 1}
foo(1, y=2, **{'a': 1, 'b': 2, 'c': 3}) #1 - 2 - b c a - {'b': 2, 'c': 3, 'a': 1}
foo(**{'z': 3, 'x': 1, 'y': 1}) #1 - 1 - z - {'z': 3}

#===========*args+**kwargs===========
def foo(*args, **kwargs):
    print(args,'-',*args, '-', kwargs,'-', *kwargs)
foo(1, y=2, a=1, b=2, c=3) #(1,) - 1 - {'c': 3, 'b': 2, 'y': 2, 'a': 1} - c b y a


#===========*後定義的參數,必須被傳值(有默認值的除外)且必須按照關鍵字實參的形式傳遞===========
def foo(x,y,*args,b,a=1,**kwargs):
    print(x, '-', y, '-', *args, '-', args,'-',a, '-', b,'-', kwargs,'-', *kwargs)
foo(1,2,3,4,5,b=3,c=4,d=5) #1 - 2 - 3 4 5 - (3, 4, 5) - 1 - 3 - {'c': 4, 'd': 5} - c d

函數對象

函數是第一類對象,即函數能夠看成數據傳遞數據結構

  • 能夠被引用
  • 能夠看成參數傳遞
  • 返回值能夠是函數
  • 能夠看成容器類型的元素
?
1
2
3
4
5
6
7
8
9
10
11
12
13
def foo():
     print ( 'foo' )
def bar():
     print ( 'bar' )
dic = {
     'foo' :foo,
     'bar' :bar,
}
while True :
     choice = input ( '>>: ' ).strip()
     if choice in dic:
         print (dic[choice], type (dic[choice])) # <function foo at 0x00000000010EF9D8> <class 'function'>
         dic[choice]()     #加括號就運行函數

函數嵌套

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def least(x,y):
     return x if x > y else y
 
def max4(a,b,c,d):
     res1 = least(a,b)
     res2 = least(res1,c)
     res3 = least(res2,d)
     return res3
print (max4( 1 , 2 , 3 , 4 ))  #4
 
def f1():
     def f2():
         def f3():
             print ( 'from f3' )
         f3()
     f2()
 
f1() #from f3

名稱空間與做用域

1、什麼叫名稱空間

存放名字的地方,三種名稱空間,(x=1,1存放於內存中,那名字x存放在哪裏呢?名稱空間正是存放名字x與1綁定關係的地方)閉包

2、名稱空間的加載順序

  • python解釋器先啓動,於是首先加載的是:內置名稱空間 builtins(內置模塊的名字空間)
  • 執行test.py文件,而後以文件爲基礎,加載全局名稱空間 globals(全局變量,函數定義所在模塊的名字空間)
  • 外部嵌套函數的名字空間 enclosing
  • 在執行文件的過程當中若是調用函數,則臨時產生局部名稱空間 locals(是函數內的名稱空間,包括局部變量和形參)

3、名字的查找順序

  • LEGB 表明名字查找順序: locals -> enclosing function -> globals -> __builtins__
  • 局部名稱空間--->全局名稱空間--->內置名稱空間
  • 在全局沒法查看局部的,在局部能夠查看全局的

4、做用域即範圍

  • 全局範圍(內置名稱空間與全局名稱空間屬於該範圍),全局有效
  • 局部範圍(局部名稱空間屬於該範圍),局部有效
  • *****做用域關係是在函數定義階段就已經固定的,與函數的調用位置無關*****
?
1
2
3
4
5
6
7
8
#若是函數收到的是一個不可變對象(好比數字、字符或者元組)的引用,就不能直接修改原始對象,
# 至關於經過「傳值’來傳遞對象,此時若是想改變這些變量的值,能夠將這些變量申明爲全局變量。
num = 20
def show_num(x = num):
     print (x)
show_num()  #20
num = 30
show_num()  #20
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
name = "jack"   #全局變量
age = 90 #全局變量
gender = 'male' #全局變量
def out():
     name = 'tom' #局部變量
     age = 18   #局部變量
     def inner():
         global name  #global聲明此時的name是全局的
         name = "rose" #修改全局name
         print (name, 'inner' ) #rose inner 讀取全局的
         print (age, 'inner' ) # 18 inner 本身沒有向外找 out裏面有就讀取,
         print (gender, 'inner' ) # male inner 本身沒有向外找,out裏也沒有,再向外找,找到全局的
     inner()
     print (name, 'out' ) #tom out  先找本身,沒有在向外找,本身有就讀取
     print (age, 'out' ) #18 out  先找本身,沒有在向外找,本身有就讀取
     print (gender, 'out' ) #male out  先找本身,沒有在向外找,找到全局的
 
out()
 
print (name, 'gl' #rose gl 被inner修改了
print (age, 'gl' #90 gl
print (gender, 'gl' #male gl
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
"""
LEGB 表明名字查找順序: locals -> enclosing -> globals -> __builtins__
locals 是函數內的名字空間,包括局部變量和形參
enclosing 外部嵌套函數的名字空間(閉包中常見)
globals 全局變量,函數定義所在模塊的名字空間
builtins 內置模塊的名字空間
"""
x = 1
def f1():
     def f2():
         print (x)
     return f2
x = 100
def f3(func):
     x = 2
     func()
x = 10000
f3(f1()) # locals -> enclosing -> globals ->打印10000
 
name = 'tom'
def change_name():
     global name   #global 聲明此時的name是全局的
     name = 'rose'   #修改了全局的name
     print ( 'change_name' ,name)
change_name()  #打印change_name rose
print (name) #rose
 
li = [ "aa" , "bb" ]
def fun():
     global li
     li = [ "cc" ]
     li.append( 'bye' )
     print ( 'fun內' , li)
fun() #fun 內 ['cc', 'bye']
print (li, type (li)) #['cc', 'bye'] <class 'list'>
 
li = [ "aa" , "bb" ]
def fun():
     li = [ "cc" ]
     li.append( 'bye' )
     print ( 'fun內' , li)
fun() #fun內 ['cc', 'bye']
print (li, type (li)) #['aa', 'bb'] <class 'list'>
 
li = [ "aa" , "bb" ]
def fun():
     li.append( 'bye' )
     print ( 'fun內' , li)
fun() #fun內 ['aa', 'bb', 'bye']
print (li, type (li)) #['aa', 'bb', 'bye'] <class 'list'>

閉包函數

  • 建立閉包函數必須知足3點:
    1. 一、必須有一個內嵌函數
    2. 二、內嵌函數必須引用外部函數中的變量(非全局做用域的引用)
    3. 三、外部函數的返回值必須是內嵌函數
  • 閉包意義:
    1. 以前咱們都是經過參數將外部的值傳給函數,閉包提供了另一種思路
    2. 返回的函數對象,在該函數外還包裹了一層做用域,這使得,該函數不管在何處調用,優先使用本身外層包裹的做用域
  • 應用領域:
    1. 延遲計算
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
def count():
     n = 0
     def fun():
         nonlocal n   # nonlocal,指定上一級變量,若是沒有就繼續往上直到找到爲止
         n + = 1
         return n
     print (n, '===' )
     return fun
 
c = count()
print (c())
print (c())
print (c())
c = count()
"""
0 ===
1
2
3
0 ===
"""
 
from urllib.request import urlopen
def index(url):
     def get():
         return urlopen(url).read()
     return get
baidu = index( 'http://www.baidu.com' )
print (baidu) #<function index.<locals>.get at 0x0000000002CC59D8>
print ( type (baidu())) #<class 'bytes'>
print (baidu().decode( 'utf-8' ))

遞歸函數

遞歸調用是函數嵌套調用的一種特殊形式,函數在調用時,調用了自身,就是遞歸調用。app

  • 必須有一個明確的結束條件
  • 每次進入更深一層遞歸時,問題規模相比上次遞歸都應有所減小
  • 遞歸效率不高,遞歸層次過多會致使棧溢出(在計算機中,函數調用是經過棧(stack)這種數據結構實現的,每當進入一個函數調用,棧就會加一層棧幀,每當函數返回,棧就會減一層棧幀。因爲棧的大小不是無限的,因此,遞歸調用的次數過多,會致使棧溢出)
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def f(n):
     if 0 = = n:                   # n=0 的話直接返回空,對用戶輸入的零進行判斷
         return None
     elif 1 = = n:                 # n=1 的話就再也不遞歸
         return n
     else :
         return n * f(n - 1 )       # 遞歸在執行f(n-1) 直到f(1)
print (f( 5 ))                   # 120
'''
     f(5)的執行過程以下
         ===> f(5)
         ===> 5 * f(4)
         ===> 5 * (4 * f(3))
         ===> 5 * (4 * (3 * f(2)))
         ===> 5 * (4 * (3 * (2 * f(1))))
         ===> 5 * (4 * (3 * (2 * 1)))
         ===> 5 * (4 * (3 * 2))
         ===> 5 * (4 * 6)
         ===> 5 * 24
         ===> 120
'''
相關文章
相關標籤/搜索