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