【函數】01、函數基礎

一、函數

1、函數

  函數是python中組織代碼的最小單元

   函數是實現模塊化編程的基本組件

   Python使用def語句定義函數

  每個Python函數都有一個返回值,默認爲None,也可以使用“return value”明確定定義返回值

  def語句會創建一個函數對象,並同時創建一個指向函數的對象引用

  函數也是對象,可以存儲在組合數據類型中,也可以作爲參數傳遞給其它函數

  callable()可用於測試對象是否可調用


  函數通過def定義,接着函數名,函數名後面用一對小括號列出參數列表,使用一個冒號開始函數體。

  函數體是正常的Python語句,可以組合任意結構

  return語句表示函數的返回值

  函數有輸入(參數)和輸出(返回值),函數其實是一個代碼單元,把輸入轉化爲輸出。

  定義函數的時候並不會執行函數體,當調用函數時纔會執行函數體

  函數通過函數名來調用,函數名後面一對小括號裏面傳入實參

In [2]: def f1():             
   ...:     print("hello")
   ...:     

In [3]: f1
Out[3]: <function __main__.f1>

In [4]: f1()
hello

In [5]: a = f1

In [6]: b = f1()
hello

In [7]: a
Out[7]: <function __main__.f1>

In [8]: b

In [9]: type(a)
Out[9]: function

In [10]: type(b)
Out[10]: NoneType

In [15]: callable(a)   # 實現了__call__()方法就可調用
Out[15]: True


2、對於Python的函數,我們需要記住的是

 1)函數的默認返回值是None。

 2)python是一個自上而下逐行解釋並執行的語言。因此,函數的定義必須在函數被調用之前。同名的函數,後定義的會覆蓋前面定義的。

 3)程序執行的時候,遇到函數定義只會先將函數整體讀進內存,並不立刻執行。等到函數被調用的時候才執行函數體。

 4)python函數的參數傳遞的是值傳遞還是引用傳遞。

    函數參數傳遞本質上和變量整體複製一樣,只是兩個變量分別爲形參a和實參b。那麼,a=b後,a變了,b值是否跟着變呢?這取決於對象內容可變不可變


值傳遞:

     在調用函數時,將實際參數複製一份傳遞給函數,函數對參數進行修改將不會影響到實際參數

     適用於不可變對象(如int, str,tuples等)作爲參數傳遞時,例如元組

引用傳遞

     指調用函數時,將實際參數的地址傳遞給函數,函數對參數進行修改,將影響實際參數

     適用於可變對象(如list,dict,類的實例等)作爲參數傳遞時,例如列表


淺複製:(也叫影子複製)

      只複製父對象,不會複製對象的內部的子對象
深複製

      複製對象及其子對象

賦值是引用傳遞

In [74]: l1 = [1, 2, 3]

In [75]: l2 = l1

In [76]: l2
Out[76]: [1, 2, 3]

In [77]: l3 = l1.copy()

In [78]: l3
Out[78]: [1, 2, 3]

In [79]: id(l3)             # l3和l1應用的是不同內存對象
Out[79]: 140149284135624

In [80]: id(l1)
Out[80]: 140149295996616

In [81]: id(l2)              # l2和l1引用的是同一個內存對象
Out[81]: 140149295996616


二、函數的參數

對於函數,最重要的知識點莫過於參數了。

參數分爲形式參數(形參)和實際參數(實參)。

 def f1(a, b, c):
    pass
  
  
 f1(1, 2, 3)

  其中,a,b,c就是形參,1,2,3就是實參,也就是實際要傳遞的參數。


In [27]: def add(x, y):
    ...:     print(x + y)
    ...:     return x  + y
    ...: 

In [28]: add(3, 5)
8
Out[28]: 8

In [29]: add(3)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-29-c9889e45a711> in <module>()
----> 1 add(3)

TypeError: add() missing 1 required positional argument: 'y'

In [30]: add(3, 5, 8)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-30-0b52ebc12bc6> in <module>()
----> 1 add(3, 5, 8)

TypeError: add() takes 2 positional arguments but 3 were given


In [42]: add(3, "5")
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-42-d73b87d3f8c1> in <module>()
----> 1 add(3, "5")

<ipython-input-31-4950d8722875> in add(x, y)
      1 def add(x, y):
----> 2     ret = x + y
      3     print('{} + {} = {}'.format(x, y, ret))
      4     return ret

TypeError: unsupported operand type(s) for +: 'int' and 'str'

函數調用時,傳入的實參必須和函數定義時的行參想匹配,如果不匹配會拋出TypeError.

Python中的形式參數有以下幾種:


1、位置參數

  通常在傳遞參數的時候我們按照參數的位置,逐一傳遞,這叫“位置參數”。

In [31]: def add(x, y):
    ...:     ret = x + y
    ...:     print('{} + {} = {}'.format(x, y, ret))
    ...:     return ret
    ...: 

In [32]: add(3, 5)  
3 + 5 = 8
Out[32]: 8


2、關鍵字參數

  而有時候我們會用“形參名”=“值”的方式傳遞參數,這叫“關鍵字參數指定參數”。

In [33]: add(x=3, y=5)
3 + 5 = 8
Out[33]: 8

In [34]: add(y=3, x=5)  # 關鍵字參數和順序無關
5 + 3 = 8
Out[34]: 8


位置參數和關鍵字參數混用時關鍵字參數必須在位置參數後面

In [37]: add(3, y=5)
3 + 5 = 8
Out[37]: 8

In [38]: add(x=3, 5)
  File "<ipython-input-38-165b39de39ac>", line 1
    add(x=3, 5)
            ^
SyntaxError: positional argument follows keyword argument


In [39]: add(3, x=5)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-39-b0f1746a3413> in <module>()
----> 1 add(3, x=5)

TypeError: add() got multiple values for argument 'x'


3、默認參數

  默認參數是爲某些參數設定一個默認值,既可以減少參數傳入量,也可以享受使用默認值的便利。

默認參數必須位於參數列表的最後部分

默認參數是在函數定義時,指的是形參!

In [46]: def inc(base, x=1):
    ...:     return base + x
    ...: 

In [47]: inc(3, 2)
Out[47]: 5

In [48]: inc(3)
Out[48]: 4

In [49]: inc(3, x=4)
Out[49]: 7

In [56]: inc(base=4)
Out[56]: 5

In [57]: inc(base=4, x=5)
Out[57]: 9


In [50]: def inc(x=1, base):
    ...:     return base + x
    ...: 
  File "<ipython-input-50-ac010ba50fd9>", line 1
    def inc(x=1, base):
           ^
SyntaxError: non-default argument follows default argument


4、可變(動態)參數

 Python的動態參數有兩種,分別是*args和**kwargs,這裏面的關鍵是一個和兩個星號,而不是args和kwargs,實際上你可以使用*any或**whatever的方式,但就如self一樣,潛規則我們使用*args和**kwargs。

可變參數是在函數定義時,指的是形參!


*args位置可變參數;函數定義時參數名前加一個星號

           一個星號表示接受任意個動態參數。調用時,會將實際參數打包成一個元組傳入函數

           此時只能通過位置參數傳參

In [63]: def sum(*args):
    ...:     ret = 0
    ...:     for i in args:
    ...:         ret += i 
    ...:     return ret
    ...: 

In [64]: sum()
Out[64]: 0

In [65]: sum(1)
Out[65]: 1

In [66]: sum(1, 2, 3, 8)
Out[66]: 14


**kwargs關鍵字可變參數;函數定義時參數名前加兩個星號

                  兩個星表示接受鍵值對的動態參數,數量任意。調用的時候會將實際參數打包成字典

                  此時只能通過關鍵字參數傳參

例如:

In [73]: def connect(**kwargs):
    ...:     print(type(kwargs))
    ...:     for k, v in kwargs.items():
    ...:         print('{} => {}'.format(k, v))
    ...:         

In [74]: connect(host="localhost", port=3300)
<class 'dict'>
host => localhost
port => 3300


In [79]: connect("localhost", port=3300)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-79-6aeadd0e08a5> in <module>()
----> 1 connect("localhost", port=3300)

TypeError: connect() takes 0 positional arguments but 1 was given


萬能參數:

 當*args和**kwargs組合起來使用,理論上能接受任何形式和數量的參數,在很多代碼中我們都能見到這種定義方式。需要注意的是,*args必須出現在**kwargs之前。

In [83]: def fn(*args, **kwargs):
    ...:     print(args)
    ...:     print(kwargs)
    ...:     

In [84]: fn(2, 3, 5, x=11, y="xxj")
(2, 3, 5)
{'x': 11, 'y': 'xxj'}

In [85]: def fn(**kwargs, *args):
    ...:     print(args)
    ...:     print(kwargs)
  File "<ipython-input-85-ae6e58836952>", line 1
    def fn(**kwargs, *args):
                     ^
SyntaxError: invalid syntax


可變參數和普通參數混合使用:

In [91]: def fn(x, y, *args, **kwargs):
    ...:     print(x)
    ...:     print(y)
    ...:     print(args)
    ...:     print(kwargs)
    ...:     

In [92]: fn(2, 3, 4, 5, 6, a=1,b=2)
2
3
(4, 5, 6)
{'a': 1, 'b': 2}


In [95]: fn(2, 3)
2
3
()
{}


In [97]: fn(2, y=3)
2
3
()
{}


In [99]: fn(2, 3, 4)
2
3
(4,)
{}

In [100]: fn(2, 3, a=23)
2
3
()
{'a': 23}


In [107]: def fn(*args, x):    # 位置可變參數是否可以在普通參數之前
     ...:     print(args)
     ...:     print(x)
     ...:     

In [108]: fn(1, 2, 3)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-108-9ab6b0a400c3> in <module>()
----> 1 fn(1, 2, 3)

TypeError: fn() missing 1 required keyword-only argument: 'x'

In [109]: fn(1, 2, x=3)
(1, 2)
3

In [110]: fn(1, x=3)
(1,)
3

In [111]: fn(x=3)
()
3


In [112]: def fn(**kwargs, x):     # 關鍵字可變參數是否可以在普通參數之前
     ...:     print(kwargs)
     ...:     print(x)
     ...:     
  File "<ipython-input-112-3226e57525e0>", line 1
    def fn(**kwargs, x):
                     ^
SyntaxError: invalid syntax

位置可變參數也可以在普通參數之前,但是在位置可變參數之後的普通參數變成了keyword-only參數(只能以關鍵字參數登入)

關鍵字可變參數不可以在普通參數之前(爲什麼?)



可變參數和默認參數混合使用:

In [113]: def fn(x=5, *args):
     ...:     print(x)
     ...:     print(args)
     ...:     

In [114]: fn(1)
1
()

In [115]: fn(1,2)
1
(2,)

In [116]: fn(1,2,3,4)
1
(2, 3, 4)

In [117]: fn(x=3, 4)
  File "<ipython-input-117-2d95cecddace>", line 1
    fn(x=3, 4)
           ^
SyntaxError: positional argument follows keyword argument


In [121]: def fn(*args, x=5):  # 位置參數在默認參數之前沒限制
     ...:     print(x)
     ...:     print(args)
     ...:     

In [122]: fn()
5
()

In [123]: fn(1, 2)
5
(1, 2)

In [124]: fn(1)
5
(1,)

In [125]: fn(1, x=3)
3
(1,)


In [127]: def fn(**kwargs, x=5):
     ...:     print(x)
     ...:     print(kwargs)
     ...:    
  File "<ipython-input-127-069a751edec8>", line 1
    def fn(**kwargs, x=5):
                     ^
SyntaxError: invalid syntax


In [128]: def fn(x=5, **kwargs):
     ...:     print(x)
     ...:     print(kwargs)
     ...:     
     ...:    

In [129]: fn()
5
{}

In [130]: fn(1)
1
{}

In [131]: fn(1, a=2)
1
{'a': 2}

In [132]: fn(x=1, a=2)
1
{'a': 2}

可變位置參數在默認參數之後,默認參數不能使用關鍵字傳參

可變關鍵字參數不能在默認參數之前

當默認參數和可變參數一起出現時,默認參數相當於普通參數



小結:

函數的參數規則這麼多,頭都大了;這裏我們建議的函數參數使用用法:
1)默認參數靠後

 2)可變參數靠後

 3)默認參數和可變參數不同時出現

       不遵守不一定錯,遵守代碼可讀性高


當我們需要同時使用默認參數和可變參數時怎麼辦?

我們通常這樣處理:

In [139]: def connect(host='127.0.0.1', port=3306, user='root', password='', **kwargs):
     ...:     pass
     ...: 

     
In [140]: def connect(**kwargs):
     ...:     host = kwargs.pop('host', '127.0.0.1')
     ...:


三、參數解構

1、參數解構

調用函數時,傳入實參時加一個星號,可以把iterable解構成位置參數

調用函數時,傳入實參時加兩個個星號,可以把dict解構成關鍵字參數

In [141]: def add(x, y):
     ...:     ret = x + y
     ...:     print('{} + {} = {}'.format(x, y, ret))
     ...:     

In [142]: add(1, 2)
1 + 2 = 3

In [145]: t = [1, 2]

In [146]: add(*t)
1 + 2 = 3

In [143]: t = [1, 2, 3]

In [144]: add(*t)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-144-d90b03560d6e> in <module>()
----> 1 add(*t)

TypeError: add() takes 2 positional arguments but 3 were given


In [147]: t = [(1, 2), (3, 4)]

In [148]: add(*t)             
In [148]: add(*t)
(1, 2) + (3, 4) = (1, 2, 3, 4)


# 字典解構成關鍵字參數

In [153]: d = {'x':1, 'y':2}

In [154]: add(**d)
1 + 2 = 3

In [155]: add(*d)
x + y = xy


2、參數解構和可變參數混用

In [161]: def sum(*args):
     ...:     ret = 0
     ...:     for i in args:
     ...:         ret += i
     ...:     return ret
     ...: 

In [162]: sum(*(1, 2))
Out[162]: 3

In [163]: sum(*[1, 2])
Out[163]: 3

In [165]: sum(*range(5))
Out[165]: 10



3、參數解構的限制

  關鍵字參數解構,key必須是str

In [167]: def fn(**kwargs):
     ...:     print(kwargs)
     ...:     

In [168]: fn(**{'a':1})
{'a': 1}


In [170]: fn(**{12:1})
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-170-99986e5a722a> in <module>()
----> 1 fn(**{12:1})


In [211]: def fn(*args, **kwargs):
     ...:     print(args)
     ...:     print(kwargs)
     ...:     

In [212]: fn(*[1, 2, 3])
(1, 2, 3)
{}


In [214]: fn(*[1, 2, 3], **{'a':1, 'b':2})
(1, 2, 3)
{'a': 1, 'b': 2}


4、keyword-only參數

星號之後的參數只能通過關鍵字參數傳入

可變位置參數之後的參數也是keyword-only參數

只能通過關鍵字參數傳入的參數就交keyword-only參數

keyword-only參數可以有默認值

In [179]: def fn(*, x):
     ...:     print(x)
     ...:     

In [180]: fn(1)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-180-cb5d79cf2c77> in <module>()
----> 1 fn(1)

TypeError: fn() takes 0 positional arguments but 1 was given

In [181]: fn(x=1)
1

In [182]: fn(1, x=2)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-182-8eb9b719c886> in <module>()
----> 1 fn(1, x=2)

TypeError: fn() takes 0 positional arguments but 1 positional argument (and 1 keyword-only argument) were given

In [183]:


keyword-only參數與其它參數混用:

In [188]: def fn(x, *, y):
     ...:     print(x)
     ...:     print(y)
     ...:     

In [189]: fn(1, y=2)
1
2

In [193]: def fn(x=1, *, y=2):
     ...:     print(x)
     ...:     print(y)
     ...:     
     ...:     

In [194]: fn()
1
2


In [199]: def fn(x=1, *, y):   # 
     ...:     print(x)
     ...:     print(y)
     ...:     
     ...:    

In [200]: fn(y=3)
1
3

In [201]: fn(3)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-201-f005f2a6106f> in <module>()
----> 1 fn(3)

TypeError: fn() missing 1 required keyword-only argument: 'y'

In [202]: 


In [205]: def fn(*, x, y):   # *號後可以有多個keyword-only參數 
     ...:     print(x)
     ...:     print(y)
     ...:     

In [206]: fn(x=1, y=2)
1
2

In [207]: def fn(x, y, *):  # *號不能寫在最後
     ...:     print(x)
     ...:     print(y)
     ...:     
  File "<ipython-input-207-6c060b52ac76>", line 1
    def fn(x, y, *):
                ^
SyntaxError: named arguments must follow bare *



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章