1. 程式人生 > >使用doctest單元測試方式培訓講解:Python函數基礎

使用doctest單元測試方式培訓講解:Python函數基礎

動態 獲得 直接 return 全部 Coding 位置順序 收集 顯式

# coding = utf-8

‘‘‘
函數聲明:
def name([arg,... arg = value,... *arg, **kwarg]):
    suite

1. 當編譯器遇到 def,會生成創建函數對象指令。
   也就是說 def 是執?行指令,?不僅僅是個語法關鍵字。
   可以在任何地?方動態創建函數對象。
2. 可以使用默認參數、可變參數和關鍵字參數
        arg = value 是默認參數
        *args是可變參數,args接收的是一個tuple;
        **kwargs是關鍵字參數,kwargs接收的是一個dict。

lambda函數
不同於?用 def 定義復雜函數,lambda 只能是有返回值的簡單的表達式。使?用賦值語句會引發語法 錯誤,可以考慮?用函數代替。
‘‘‘

‘‘‘
*****************************************************
1. 函數創建
   函數是第一類對象,可作為其他函數的實參或返回值。
   函數總是有返回值。就算沒有 return,默認也會返回 None。
*****************************************************
‘‘‘
def test1(name):
    ‘‘‘

    >>> test1(‘a‘).__name__ # 查看函數返回函數的名字
    ‘a‘

    >>> test1(‘b‘).__name__ # 查看函數返回函數的名字
    ‘b‘

    >>> test1(‘a‘)()        # 調用函數返回的函數
    call function a

    >>> print(test1(‘c‘))   # 調用函數,返回2個值:整型和字符串
    (0, ‘test‘)
    ‘‘‘
    if name == "a":
        def a():
            print(‘call function a‘)
        return a
    elif name == "b":
        def b(): pass
        return b
    else:
        return 0, ‘test‘

‘‘‘
*************************************************************
2. 參數
   2.1 函數的傳參方式靈活多變,可按位置順序傳參,也可不關?順序?命名實參。
*************************************************************
‘‘‘
def test21(a, b):
    ‘‘‘

    >>> test21(1,2)      # 位置參數
    1 2

    >>> test21(b=3,a=4)  # 命名參數
    4 3
    ‘‘‘
    print(a, b)

‘‘‘
*************************************************************
   2.2 ?持參數默認值。不過要??,
       默認值對象在創建函數時生成,所有調用都使?同?對象。
       如果該默認值是可變類型,那麽就如同 C 靜態局部變量。
*************************************************************
‘‘‘
def test22(x, lst=[]):
    ‘‘‘
    >>> test22(1)
    [1]

    >>> test22(2)   # 保持了上次調?用狀態。
    [1, 2]

    >>> test22(3, [])   # 顯式提供實參,不使?用默認值。
    [3]

    >>> test22(3)   # 再次使?用默認值,會繼續使用默認的列表對象
    [1, 2, 3]

    ‘‘‘

    lst.append(x)
    return lst

‘‘‘
*************************************************************
   2.3 默認參數後?不能有其他位置參數,除非是變參。
   SyntaxError: def test23(a, b=1, c): pass

   2.4 用 *args 收集 "多余" 的位置參數,**kwargs 收集 "額外" 的命名參數。
       這兩個名字只是慣例,可 ?自由命名。
       *args是可變參數,args接收的是一個tuple;
       **kwargs是關鍵字參數,kwargs接收的是一個dict。
*************************************************************
‘‘‘
def test24(a, b=1, *args, **kwargs):
    ‘‘‘

    >>> test24(0)
    0
    1
    ()
    {}

    >>> test24(0,2,3,4)
    0
    2
    (3, 4)
    {}

    >>> test24(0,2,3,4,x=5)
    0
    2
    (3, 4)
    {‘x‘: 5}

    # 可 "展開" 序列類型和字典,將全部元素當做多個實參使?用。如不展開的話,那僅是單個實參對象。
    >>> test24(*range(5), **{‘x‘: 10, ‘y‘: 11})
    0
    1
    (2, 3, 4)
    {‘x‘: 10, ‘y‘: 11}

    >>> test24(range(5))
    range(0, 5)
    1
    ()
    {}

    # 單個 "*" 展開序列類型,或者僅是字典的主鍵列表。
    # "**" 展開字典鍵值對。但如果沒有變參收集, 展開後多余的參數將引發異常。
    >>> p = dict(a=20,b=21)

    >>> test24(p)
    {‘a‘: 20, ‘b‘: 21}
    1
    ()
    {}

    >>> test24(*p)  # 僅展開 keys(),test("a"、"b")
    a
    b
    ()
    {}

    >>> test24(**p)  # 展開 items(),test(a = 1, b = 2)。
    20
    21
    ()
    {}
    ‘‘‘

    print(a)
    print(b)
    print(args)
    print(kwargs)

‘‘‘
*************************************************************
3. 作用域
   3.1 函數形參和內部變量都存儲在 locals 名字空間中。
*************************************************************
‘‘‘
def test31(a, b=1):
    ‘‘‘

    >>> test31(100)
    {‘s‘: ‘Hello Python‘, ‘b‘: 1, ‘a‘: 100}

    ‘‘‘

    s = ‘Hello Python‘
    print(locals())

‘‘‘
*************************************************************
    3.2 除?使用 global、nonlocal 特別聲明,
      否則,在函數內部使用賦值語句,總是在 locals 名字空間中 新建一個對象關聯。
      註意:"賦值" 是指名字指向新的對象,??通過名字改變對象狀態。

      名字查找順序: locals -> enclosing function -> globals -> __builtins__
    ? locals: 函數內部名字空間,包括局部變量和形參。
    ? enclosing function: 外部嵌套函數的名字空間。
    ? globals: 函數定義所在模塊的名字空間。
    ? __builtins__: 內置模塊的名字空間。

      如果不修改全局變量也可以不使用global關鍵字。
      Python3 提供了 nonlocal 關鍵字,用來修改外部 嵌套函數名字空間, python2.7 沒有。
      nonlocal關鍵字用來在函數或其他作用域中使用外層(非全局)變量。
*************************************************************
‘‘‘
x, y = 1, 2  # 在模塊級別直接定義的全局變量

def test321():
    global x    # 引用全局變量
    x = 11
    y = 21      # 這裏的 y 是局部變量
    global z    # 在函數內部定義一個全局變量,而沒有在模塊級別定義
    z = 3

    print(‘ in test321(): x=%d; y=%d; z=%d‘ % (x, y, z))

def test322():
    print(‘ in test322(): x=%d; y=%d ; z=%d‘ % (x, y, z))    # 直接訪問所有全局變量,包括變量z

# 測試
print(‘        out 1: x=%d; y=%d;  z=undefine‘ % (x, y))
test321()
test322()
print(‘        out 2: x=%d; y=%d ; z=%d‘ % (x, y, z))


‘‘‘
*************************************************************
3.3 閉包
    閉包(closure)是函數式編程的重要的語法結構。
    閉包也是一種組織代碼的結構,它同樣提高了代碼的可重復使用性。
*************************************************************
‘‘‘

def lineConfig(a,b):
    ‘‘‘
    這個例子中,函數line與環境變量a,b構 成閉包。
    在創建閉包的時候,我們通過line_conf的參數a,b說明了這兩個環境變量的取值,
    這樣我們就確定了函數的最終形式(y = x + 1和y = 4x + 5)。
    我們只需要變換參數a,b,就可以獲得不同的直線表達函數。
    由此,我們可以看到,閉包也具有提高代碼可復用性的作用。

    >>> y1 = lineConfig(1, 1)   # 定義直線 y = x + 1
    >>> p1 = y1(2)              # 計算y坐標,獲取x=2時y1的值
    >>> print(p1)
    3

    >>> lstPoint = [y1(x) for x in range(3)] # 計算y坐標列表
    >>> print(lstPoint)
    [1, 2, 3]

    >>> y2 = lineConfig(4, 5)   # 定義直線 y = 4x + 5
    >>> p2 = y2(x=2)            # 計算y坐標,獲取x=2時y2的值
    >>> print(p2)
    13
    ‘‘‘

    def line(x):
        return a*x + b

    return line


def newCounter(i=0):
    ‘‘‘
    兩個獨立的計數器
    >>> c1 = newCounter()
    >>> c2 = newCounter(2)
    >>> print(c1(),c1(),c1())
    1 2 3

    >>> print(c2(),c2(),c2())
    3 4 5

    ‘‘‘

    def counter():
        nonlocal i
        i = i + 1
        return i
    return counter

if __name__ == ‘__main__‘:
    import doctest
    doctest.testmod(verbose=True)

  

使用doctest單元測試方式培訓講解:Python函數基礎