天天看點

python函數詳解寫在篇前函數參數特殊函數内置函數

寫在篇前

  函數能提高應用的子產品性,和代碼的重複使用率,是程式設計必須具備的基本抽象能力。python函數更是奇妙靈活,與很多特性值得探讨,本篇文章就來詳細看看python 函數那些巧妙之處。首先,在篇前簡單說說python函數的形式。比如說我們要實作一個函數:計算一個數m的n次方,那麼函數可以定義如下:

def exponentiation(m, n):
    return m^n

# def 是函數定義關鍵字
# exponentiation 是函數名
# (m,n)是函數參數,後面冒号之後是函數體
# return 表示傳回值,一個函數也可以沒有函數體,此時傳回值為None
           

函數參數

  • 必選參數

     必選參數,又稱為位置參數,我們上面定義的函數

    exponentiation

    m, n

    就是必選參數。當調用該函數時,每一個必選參數都必須傳入合适的值。
  • 預設參數

     修改函數

    exponentiation

    為:
    def exponentiation(m, n=2):
        return m^n
               
     這樣,

    n

    便是一個預設參數,當計算一個數的二次方時,隻需要傳入參數

    m

    即可:
    >>> exponentiation(3)
    9
               
     需要注意的是,必選參數必須在前,預設參數在後,否則Python的解釋器會報錯;為了避免不必要的坑,預設參數必須指向不變對象。 另外,在函數定義的時候就會确定預設參數的值,當我們改變x的值的時候對預設參數值并沒有影響,即:
    >>> x = 42
    >>> def spam(a, b=x):
    ...     print(a, b)
    ...
    >>> spam(1)
    1 42
    >>> x = 23
    >>> spam(1)
    1 42
    >>>
               
  • 可變參數

      可變參數是python函數靈活性的表現之一,如果有這樣一個需求,編寫一個函數計算若幹個數的和,這時可變參數便是一種好的選擇:

    def calc_sum(*args):
        sum = 0
        for arg in args:
            sum += arg
        return sum
    
    
    print(calc_sum(1, 2, 3)) # 可變參數可傳入0個或任意個參數,在函數調用時自動組裝為一個tuple
    nums = [1, 2, 3]
    print(calc_sum(*nums))  # *nums意思是将nums中的所有元素以可變參數的形式傳入函數
               
  • 關鍵字參數

     關鍵字參數允許你傳入0個或任意個含參數名的參數,在函數内部會自動組裝為一個dict。

    def print_info(pc_id, pc_name, **kwargs):
        print(pc_id, pc_name, kwargs, sep='\n')
    
    
    other_info = {'city': 'NanJing', 'company': 'APPLE'}
    print_info('0001', 'IMac-2018', city='NanJing', company='APPLE')
    print_info('0001', 'IMac-2018', **other_info)
    
    0001
    IMac-2018
    {'city': 'NanJing', 'company': 'APPLE'}
    0001
    IMac-2018
    {'city': 'NanJing', 'company': 'APPLE'}
               

 關鍵字參數可以傳入任意參數名,那我們可不可以限制他傳入指定的參數名呢?答案是當然可以,采用命名關鍵字可以實作該需求。但是這裡需要搞清楚一個邏輯上的問題。這裡的指定的參數名要和前面的必選參數區分開來。命名關鍵字參數如果有預設值可以不傳入,但是如果傳入就隻能是已經限定的關鍵字參數。

def print_info(pc_id, pc_name, *, city, company):
    # 用一個 *,區分必選參數和關鍵字參數
    print("pc_id: %s\tpc_name: %s" % (pc_id, pc_name), end='\t')
    if city:
        print('city:%s' % city, end='\t')
    if company:
        print('company:%s' % company)


other_info = {'city': 'NanJing', 'company': 'APPLE'}

print_info('0001', 'IMac-2018', city='NanJing', company='APPLE')
print_info('0001', 'IMac-2018', **other_info)

# 但是如果函數定義中已經有了一個可變參數,命名關鍵字參數就不再需要分隔符*了
# def print_info(pc_id, pc_name, *args, city, company):
           
  • 小結

    以上四種參數可以靈活的進行組合應用,需要注意的是,參數定義的順序必須是:必選參數、預設參數、可變參數、(命名關鍵字參數)關鍵字參數。

    def f(a, b, c=0, *args, **kw):
        print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)
               

特殊函數

匿名函數

  在python中可以通過

lambda

來定義匿名函數,其傳回一個函數表達式,類似于def,但是比def更輕巧,可以沒有名字。

# 關鍵字`lambda`表示匿名函數,冒号前面的`x`,`y`表示函數參數
# 匿名函數有個限制:隻能有一個表達式,不用寫return,傳回值就是該表達式的結果。
>>>add = lambda x,y: x+y
>>>add(2,3)
5
           

 需要注意的是,lambda表達式中的參數

x、y

等是自由變量, 在運作時綁定值,而不是定義時就綁定,這跟函數的預設值參數定義是不同的。 是以在下面例子中,調用這個lambda表達式的時候,x的值是執行時的值。

>>> x = 10
>>> a = lambda y: x + y
>>> x = 20
>>> b = lambda y: x + y
>>> a(10)
30
>>> b(10)
30
           

高階函數

  一個函數接收另一個函數作為參數,這種函數稱之為高階函數(Higher-order Functions)。python中有幾個常用的内置高階函數:

  • filter

    filter()

    函數接收兩個參數,一個函數和一個序列,其中傳入的函數會依次作用于序列的每一個元素,根據傳回值是True或則False決定是否保留該元素。
    filter(lambda x: x>10, [1, 56, 3, 36, 9])
    
    
    >>> g = filter(lambda x: x>10, [1, 56, 3, 36, 9])
    >>> g
    <filter object at 0x10207b470>
    >>> type(g)
    <class 'filter'>
    >>> list(g)
    [56, 36]
               
  • map/reduce

      

    map()

    函數接收兩個參數,一個是函數,一個是

    Iterable

    map

    将傳入的函數依次作用到序列的每個元素,并把結果作為新的

    Iterator

    傳回;

    reduce()

    把一個函數作用在一個序列[x1, x2, x3, …]上,這個函數必須接收兩個參數,reduce把結果繼續和序列的下一個元素做累積計算。
    >>> r = map(lambda x:x*x, [1, 2, 3, 4, 5, 6, 7, 8, 9])
    >>> type(r)
    <class 'map'>
    >>> list(r)
    [1, 4, 9, 16, 25, 36, 49, 64, 81]
    
    from functools import reduce
    >>>reduce(lambda x,y: x+y, [1,2,3,4])
    10
               
  • sorted

      用于可疊代對象的排序,如:

    # 執行個體1
    >>> sorted([36, 5, -12, 9, -21], key=abs)
    [5, 9, -12, -21, 36]
               
  • 傳回函數

      高階函數除了可以接受函數作為參數,也可以将函數作為傳回值,實作“延遲計算”。

    def calc_sum(lst):
        def lazy_sum():
            return sum(lst)
        return lazy_sum
    
    >>> f = calc_sum([1, 2, 3, 4])
    >>>f
    <function calc_sum.<locals>.lazy_sum at 0x10e9bd158>
    >>>f()
    10
               

偏函數

  偏函數的作用是為函數某些參數設定預設值,使調用更加友善,以下會是一個好的例子:

points = [ (1, 2), (3, 4), (5, 6), (7, 8) ]
import math
def distance(p1, p2):
    x1, y1 = p1
    x2, y2 = p2
    return math.hypot(x2 - x1, y2 - y1)
>>> pt = (4, 3)
>>> points.sort(key=partial(distance,p2=pt))  # 注意這裡必須是p2=pt
>>> points
[(3, 4), (1, 2), (5, 6), (7, 8)]
           

回調函數

  在計算機程式設計中,回調函數,是指通過函數參數傳遞到其它代碼的,某一塊可執行代碼的引用。這一設計允許了底層代碼調用在高層定義的子程式。關于了解可以參考回調函數是什麼。這裡我們也給出一個例子輔助了解:

def greeting(name):
    print('hello %s!' % name)
    
def someone_coming(callback):
    name = input()
    callback(name)
    
def main():
    someone_coming(greeting)
    
if __name__ == '__main__':
    main()
           

内置函數

  這裡确切的應該說是其他内置函數,因為上面也涉及很多内置函數,如

sorted

filter

map

等。另外,python内置函數非常之多,如果想了解更全面的内部函數,可以參考内置函數1,内置函數2

  • zip()

     用于将可疊代的對象作為參數,将對象中對應的元素打包成一個個元組,然後傳回由這些元組組成的清單。需要注意的是,當多個可疊代資料length不同時,取最小長度,其他忽略。

    >>>a = [1,2,3]
    >>> b = [4,5,6]
    >>> c = [4,5,6,7,8]
    >>> zipped = zip(a,b)     # 打包為元組的清單
    [(1, 4), (2, 5), (3, 6)]
    >>> zip(a,c)              # 元素個數與最短的清單一緻
    [(1, 4), (2, 5), (3, 6)]
    >>> zip(*zipped)          # 與 zip 相反,*zipped 可了解為解壓,傳回二維矩陣式
    [(1, 2, 3), (4, 5, 6)]
               
  • reversed()

     用于反轉序列,生成新的可疊代對象

    >>> a = reversed(range(10)) # 傳入range對象
    >>> a # 類型變成疊代器
    <range_iterator object at 0x035634E8>
    >>> list(a)
    [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
               
  • enumerate()

     enumerate() 函數用于将一個可周遊的資料對象(如清單、元組或字元串)組合為一個索引序列,同時列出資料和資料下标,一般用在 for 循環當中

    >>>seq = ['one', 'two', 'three']
    >>> for i, element in enumerate(seq):
    ...     print i, element
    
    0 one
    1 two
    2 three