函數主要做用爲了複用python
函數中的return定義數據結構
函數中,全部的語句都是有retrun操做,若是函數沒有自定義的return,則默認retrun None值閉包
形參和實參 app
參數中,是一種形式的表示,一種符號的表達簡稱形參;而調用時所傳達的參數列表爲實實在在傳入的值,簡稱爲實參ide
def fn(xx): #形參函數
return xxui
print(fn('hello')) #實參數this
callableurl
使用callbale能夠看其是不是可被調用對象spa
例:
def add(x,y):
return 1
print(callable(add))
True
函數參數
測定函數須要匹配定義個數相匹配(可變參數例外)
位置參數
調用使用:按照要求是按照順序去定義傳入實參
In [1]: def f(x,y):
...: print(x,y)
...:
In [2]: f('a','ccc')
a ccc
關鍵字參數
調用使用對應的形參明確指定要傳入的參數
使用形式參數名字傳入實參的方式,順序是能夠和定義的順序不同,由於是經過名稱去定義
In [3]: f(y='python', x='hello')
hello python
關鍵字傳參
In [5]: def f(x,y,z):
...: print(x,y,z)
調用
In [7]: f(z=None,y=10,x=[1])
返回值:
[1] 10 None
In [8]: f((1,),z=6,y=4.1)
返回值:
(1,) 4.1 6
位置參數必須在關鍵字參數以前傳入,不然會被誤認
In [13]: f((1,),z=6,1)
File "<ipython-input-13-452adbde2693>", line 1
f((1,),z=6,1)
^
SyntaxError: positional argument follows keyword argument
def add(x,y):
result = x + y
print(result)
add(6,7)
13
#將關鍵字參數先傳進會提示報錯
add(x=6,7)
add(x=6,7)
^
SyntaxError: positional argument follows keyword argument
總結:簡單來說,有默認值的參數,須要在後面進行調用
通常流程來說,用戶必需要填寫的參數,直接定義爲位置參數
例:
def add(x,y=9):
result = x + y
print(result)
add(6) #這樣x就被定義爲必須定義的對象
定義一個login函數,參數命名爲host,port,username,password
def login(host='127.0.0.1',port='8080',username='chaoye',password='q1w2e3'):
print('{}:{}:{}/{}'.format(host,port,username,password))
login()
127.0.0.1:8080:chaoye/q1w2e3
login('192.168.0.1',9909,username='haha')
192.168.0.1:9909:haha/q1w2e3
login('192.168.0.1',9909,password='this')
192.168.0.1:9909:chaoye/thi
*name 可變參數
傳遞參數,能夠匹配任意個(0個到n個)參數
def fn(*args):
print(type(args))
print(args)
fn(1,2,3,4,88)
<class 'tuple'>
(1, 2, 3, 4, 88)
fn()
<class 'tuple'> # 可變參數在封裝過程當中,將其以元組的方式進行傳遞
()
def fn(*nums):
sum = 0
for x in nums:
sum+=x
print(sum)
fn(10)
10
add(1,2,3,4,88) 這裏定義表示能夠接收多個參數,這裏形參能夠接受0個到多個實參
在傳遞的過程當中,可變參數在封裝過程當中,將其以元組的方式進行傳遞,由於傳遞的同時已知其須要傳遞的個數,由於內部是不容許改變nums
*傳遞是一個不可變的類型(元組)
驗證return
In [5]: def add(*nums):
...: sum = 0
...: for x in nums:
...: sum += x
...: print(sum)
...: #retrun sum
...:
In [6]: val = add(3,5,7)
15
In [8]: print(val)
None
這裏沒有定義return,則解釋器直接調用隱含的return None進行返回
自定義return
In [12]: def add(*nums):
...: sum = 0
...: for x in nums:
...: sum += x
...: return(sum)
In [14]: add(0)
0
Out[14]: 0
**kwargs 可變關鍵字參數
**kwargs 將傳遞進來的值封裝爲字典類型,格式以下:
def showconfig(**name):
print(name)
showconfig(a=5,b=32,hj=90)
調用:
調用的時候須要對關鍵字進行指定值的賦予
{'a': 5, 'b': 32, 'hj': 90}
例:
def showconfig(**kwagrs):
for k,v in kwagrs.items():
print(k,v)
showconfig(hostname='127.0.0.1',port='8080',username='chaoye',passwd='134qwe')
hostname 127.0.0.1
username chaoye
port 8080
passwd 134qwe
以上,傳遞的信息是未知的,多是n個,因此須要使用可變類型去接收
咱們須要肯定這個值傳遞給那些kv,因此須要key=value 這樣進行賦值
傳遞以後會造成一個字典,咱們只須要對字典進行操做便可
混合使用
將關鍵字參數提取,好比username,passwd ,由於有些參數爲用戶必填,因此將其單獨分離出來,其餘所有給予默認值
有時咱們會忘記關鍵字參數,那麼能夠只寫敏感部分,好比監控端口,用戶密碼等
def showconfig(username,password,**kwagrs):
print(username)
print(password)
for k,v in kwagrs.items():
print('{} = {}'.format(k,v))
showconfig('wangchao','1234',host='127.0.0.1')
wangchao
1234
host = 127.0.0.1
改進:
def showconfig(username,*args,**kwargs):
print(username)
print(args)
for k,v in kwargs.items():
print(k,v)
showconfig('chaoye')
定義時,第一個是必須須要定義的,其餘都是可變類型,可變類型支持0到n個
定義位置的前後順序
*args 通常須要定義在**kwargs 以前的位置,以複雜度進行排放
def showconfig(username,**kwargs,*args):
print(username)
print(args)
for k,v in kwargs.items():
print(k,v)
若是定義的順序不當,則會直接告警
File "E:\python_project\a.py", line 3
def showconfig(username,**kwargs,*args):
^
SyntaxError: invalid syntax
[Finished in 0.1s with exit code 1]
總結:
·有位置可變的參數和關鍵可變的參數,*args和**kwargs
·位置可變參數和關鍵字可變參數(*args和**kwargs均可以收集若干個實例,位置可變參數(*args)收集實參封裝爲一個元組,關鍵字可變參數(**kwargs)則封裝爲一個字典
·混合使用參數的過程,可變參數要放到參數的列表最後,普通參數須要放到參數列表前,位置參數*args需放置**kwargs關鍵字參數前
即: 位置參數 --> 關鍵字參數 -->*args --> **kwargs
例:
如下語法沒有問題
def fn(*args,x,y,**kwargs):
print(x)
print(y)
print(args)
print(kwargs)
首先對位置參數進行賦值
fn(1,2,3)
提示:
TypeError: fn() missing 2 required keyword-only arguments: 'x' and 'y'
只對關鍵字參數進行賦值,可是沒有對位置參數進行傳遞,因此此類語法沒法識別
fn(3,5,a=1,b='py')
如下語句經過,首先對args進行了傳遞,x和y分別以關鍵字形式進行傳遞,b以kw的方式傳遞
fn(3,5,y=1,x=2,b='python')
2
1
(3, 5)
{'b': 'python'}
關鍵字的傳參方式依然是將x,y進行對應,原則上沒有更變,只是區別當前的傳參的方式
keyword-only 關鍵字參數形參(強制參數)
keyword-only 在python3種加入,如在一個星號形參後,或一個位置可變參數後,則是kw-only參數類型
def fn(*args,x):
print(x)
print(args)
fn(3,5,1,x=9)
args能夠手機全部的位置參數,x不能使用關鍵字參數就不可能拿到實參
若是kw放在後面是kw-only參數,kw爲可變kw,x也一樣,因此區分不出來,自己kw就跟順序無關,其本意都是收集到一個字典
*號特殊意義 後期詳細看
加上*號以後,*號將後面全部參數都強行轉爲keyword-only參數
def fn(*,x,y):
print(x,y)
fn(x=1,y=2)
這樣的定義在py3種的函數庫大量存在
例:
def fn(z,*,x,y):
print(z)
print(x,y)
fn(9,x=1,y=3)
單星號以後的參數都是kw-only參數,須要指定絕對的缺省值
通常都會給一個默認值,可是必須提醒其存在
def fn(*args,x=1):
print(x)
print(args)
可變的自己就是0個,惟一須要肯定的是在args星號後,不是普通參數,而是變身爲kw-only類型,若是須要傳參的話,至少傳3個
def connect(host='localhost',port=3306,user='admin',**kwargs):
print(host,port)
print(user)
print(kwargs)
connect(db='cmdb')
localhost 3306
admin
{'db': 'cmdb'}
connect(host='123',db='cmdb')
123 3306
admin
{'db': 'cmdb'}
函數解構
例:
def add(x,y):
print(x+y)
return x+y
add((1,2)[0],(3,4)[1])
使用*號對數據結構進行解析
def add(x,y):
print(x+y)
return x+y
t = (3,4)
add(*t)
當前變量t爲記憶參數,對應的是元組,可是須要將元組拆解
def add(x,y):
print(x,y)
t = [1,2]
add(*t)
1 2
這裏必須將其對應參數位置,不然會報錯,也就是說形參和實參數必須對應一致
t = [1,2,3,4]
add(*t)
add(*t)
TypeError: add() takes 2 positional arguments but 4 were given
使用可迭代對象
add(*range(1,3))
1 2
解構字典
如解構字典類型必須使用**kwargs的形參便可
def fn(**kwargs):
for k,v in kwargs.items():
print(k,v)
d = {'a':1,'b':2}
fn(**d)
在解構的過程,必須對應函數的位置參數
使用字典方法進行傳參:這樣就並不是是字符串傳參,而是直接將其付給形參
能夠對其進行遍歷或者取key或value進行解構
可變的類型:
def add(*iterable):
result = 0
for x in iterable:
result += x
return result
print(add(1,2,3))
print(add(*[1,2,3]))
add(*range(10))
函數返回值
return和print
return的做用是直接出棧,寫在return以後會被認爲是廢語句
print 會做爲隱含類型進行轉換並輸出
隱式調用
return = return None
def showlist():
return [1,2,3]
print(type(showlist()))
showlist返回的爲一個值,這裏爲一個列表,可是列表中是存在元素的
def showlist():
return 1,2,3
print(type(showlist()))
當沒有定義返回的數據類型,則默認將其封裝爲元組並進行返回
因此,在return的時候都是返回一個值,最後一個以元組方式進行封裝並返回
函數嵌套
內部函數不能被外部直接使用,會直接拋NameError異常
以下
def outer():
def inner():
print('inner')
print('outer')
outer()
inner()
inner()
NameError: name 'inner' is not defined
函數外是不可看到函數內部,也就是說在函數外是找不到內部定義的
做用域
函數做爲一個標識符的可見範圍,被稱爲做用域,通常來說指的是變量
例:
def fn():
out = 123 #這個變量也算爲一個標識符
print('outer')
fn()
在函數內定義的變量,在函數外是不可被調用,由於不能夠超越函數
目前咱們在全局定義變量,在函數內是能夠的
x = 50
def show():
print(x)
show()
在函數中將變量進行更改
x = 50
def show():
x += 1
print(x)
show()
x += 1
UnboundLocalError: local variable 'x' referenced before assignment
在函數本地變量中,不容許對全局變量進行操做
做用域
全局做用域
在整個程序中運行環境均可見
局部做用域
在函數內定義的變量,被稱爲本地變量,只限局部使用範圍,其餘範圍不能夠被調用
函數嵌套結構
def outer1():
o = 65
def inner():
print('inner',o)
print('outer',o)
inner()
outer1()
outer 65
inner 65
def outer2():
o = 65
def inner():
o = 97 #當進入嵌套函數中,o已經被從新定義爲一個新的變量
print('inner',o) #其打印的變量爲上行從新定義的值
print('outer',o)
inner()
outer2()
outer 65
inner 97
緣由在於賦值的定義,在外部的變量o與inner函數中的變量o沒有任何關係
外部對內部是可見的,可是內部對外部則爲不可見,在動態語言中,賦值既定義 因此都是在局部做用域中生效
x = 5
def foo():
y = x + 1
x += 1
print(x)
foo()
y = x + 1
UnboundLocalError: local variable 'x' referenced before assignment
在函數本地變量中,不容許對其進行操做,看似是y賦值時錯誤,但實際爲x+=1的時候出現錯誤
以上信息爲沒有綁定變量本地錯誤
至於x += 1爲什麼報錯的解釋以下:
由於在本地語句塊定義的x,在等式中先進行右運算,右邊有x存在,那麼被認爲
x沒有被賦值的空標識符,因此都被認爲是局部變量
全局變量global
經過global對其進行全局變量聲明
z = 1
def xx():
global z
z += 21
print(z)
xx()
22
工做流程是先調用內部,再調用外部;在函數體內經過golbal 關鍵字聲明變量;
將xx內的z使用外部的全局做用域中進行定義
賦值既定義的理念
回到以前的例子中,
x = 5
def foo():
y = x + 1
x += 1
print(x)
foo()
y = x + 1
UnboundLocalError: local variable 'x' referenced before assignment
當x = 10,在x內部做用域做爲一個外部做用域的變量賦值,因此在x+=1 的時候不會報錯,這裏的x做用域仍是全局的x+=1,先引用後賦值
而python中,賦值纔算真正的定義,才能被引用
解決辦法:在語句前從新增長賦值語句,或者使用global告知內部做用域去全局做用域中定義這個變量
內部做用域中x = 1 之類的賦值語句會從新定義局部做用域使用的變量x,可是一旦聲明全局global,聲明x爲全局的,那麼x=1至關於在全局做用域的變量x賦值
例:
z = 1
def xx():
global z
z += 21
print('xx_z:',z)
xx()
print('global_z:',z)
xx_z: 22
global_z: 22
閉包
閉包在裝飾器中使用比較普遍
閉包的概念
自由變量:
沒有在本地做用域中定義變量,在內層函數外的定義函數的自由變量
而且在因曾函數引用到了外層函數的自由變量
def counter():
c = [0]
def inc():
c[0] += 1 #僅僅是對元素在賦值,與賦值變量是兩碼事
return c[0]
return inc # 是返回一個標識符(函數的引用),可調用的對象,inc自己就是可調用對象
foo = counter()
print([foo() for _ in range(10)])
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
查看函數的類型
def counter():
c = [0]
def inner():
c[0] += 1
return c[0]
return inner
foo = counter()
print(type(foo))
<class 'function'>
按照常理c會被賦值,可是如今爲內部調用,因此在foo = counter()的時候,獲取了引用變量,引用變量+1,inner不會被銷燬,在return的時候直接return到inner()函數中進行了調用
c = 200
def counter():
c = [0]
def inner():
c[0] += 1
return c[0]
return inner
c = 200
foo = counter()
print(type(foo))
c = 200
print(foo())
print(foo())
<class 'function'>
1
2
內部函數使用了外部自由變量則產生了一個閉包
若是對外部的自由變量進行改變的話,在2版本中只能使用元素修改的方式
以下所示:
c = 200
def counter():
c = [0]
def inner():
c[0] += 1
print(c)
return c[0]
print(c[0])
return inner
foo = counter()
foo()
foo()
[0]
#如下爲閉包所產生的值
1
2
3
總結:
閉包的概念:內部函數使用了「外部自由變量」的時候產生了一個所謂比高
nonlocal
python3使用了nonlocal方式進行閉包
將變量標記在上級的局部做用域中定義,可是不能在全局做用域中定義
python2 中和 python3種的閉包對比:
python3
def outer():
count = 0
def inner():
nonlocal count
count += 1
print(count)
return count
return inner
foo = outer()
foo()
foo()
1
2
在3中,使用nonlocal只能對其能夠直接對上級變量進行操做,可是不能在全局中進行操做
python2中,僅能對自由變量進行操做
def outer():
c = [0]
def inner():
c[0] += 1
print(c)
return c
return inner
foo = outer()
foo()
foo()
默認值做用域 foo.__defaults__
涉及函數形參默認值
def foo(a=[]):
a.append(100)
print(a)
foo()
[100]
雖然是形式參數,但其也是局部變量
這個函數是一個對象,這個對象並不是被銷燬,說明對象存在,那麼將其默認值a=[]存放在一個特殊屬性上,也就是foo.__defaults__
In [2]: foo.__defaults__
Out[2]: ([],)
In [3]: foo()
[1]
In [4]: foo()
[1, 1]
在此查看默認值
In [10]: foo.__defaults__
Out[10]: ([1, 1],)
在函數外部調用並查看
In [11]: print(foo(),id(foo))
[1, 1, 1]
None 139733908903048
In [12]: print(foo.__defaults__)
([1, 1, 1],)
In [13]: print(foo(),id(foo))
[1, 1, 1, 1]
None 139733908903048
In [14]: print(foo.__defaults__)
([1, 1, 1, 1],)
In [15]: print(foo(),id(foo))
[1, 1, 1, 1, 1]
None 139733908903048
當傳遞進來lst以後,則是將引用對象傳遞,他們之間都是調用同一個id的,因此值是更改的
將更改的值return
def foo(a=None):
if a is None:
a = []
a.append(1)
return a
print(foo([1]))
print(foo())
[1, 1]
[1]
賦予函數None缺省值是一種慣例,通常形參上定義None說明接下來會傳遞某些參數進行一些處理性的操做
通常若是後期有定義或者傳遞的需求,建議將默認值寫爲None
總結:
1.使用淺拷貝建立一個新的對象,永遠不能改變傳入的參數
2.經過值判斷,靈活的選擇串講或者修改傳入的對象
方法很龍火,在不少場景下,函數定義均可以看到使用None,這個不可變的值做爲默認值進行傳參,
函數的銷燬
1.del funcname
2.覆蓋,查看地址是否一致
3.待到程序結束時