1. 程式人生 > >Python基本語法_函式屬性 & 引數型別 & 偏函式的應用

Python基本語法_函式屬性 & 引數型別 & 偏函式的應用

目錄

前言

Python除了有豐富的標準庫之外,還可以自己定義滿足自身需求的類和函式。本篇主要介紹如何建立Python自定義函式。

軟體環境

  • 系統
    • UbuntuKylin 14.04
  • 軟體
    • Python 2.7.4
    • IPython 4.0.0

Python Module的程式入口

因為一個Python的程式檔案同時也是一個模組檔案,而且我們希望可以更好的實現類和函式的程式碼重用性、節省記憶體資源、提高執行效率。即,我希望主程式僅是在被期望執行的時候才會被執行而不會自動執行。所以我們會在每一個Python module檔案中定義一個程式的入口,當我們直接執行Python module的時候會自動執行主程式。當我們一個Python module被別的程式匯入時,則僅僅會匯入在Python module檔案中定義的類和函式。
我們一般會在Python module檔案中的主程式前使用下面的一條語句:

if __name__ == '__main__':

__name__是模組的一個屬性,其作用是呼叫當前模組的名稱,若此模組是直接執行時,__name__ == ‘__main__’ 。當此模組是被其他程式import時,__name__的值為此模組的名稱。
具體的Python module檔案結構,請參考:http://blog.csdn.net/jmilk/article/details/48573995
Update: 2016-10-09
Python module程式入口最大的好處在於我們可以利用上述的這個特性來為一個 Module 編寫測試程式碼, 當我們直接執行這個 module 檔案的時候, 我們可以通過測試程式碼的實現來檢驗我們所定義的函式的輸出和輸出是否正確, 為每一個由必要測設的 module 檔案寫上測試程式碼是一個優秀的程式設計師所具備的程式設計習慣.

函式的屬性

函式是對程式邏輯進行結構化或過程化的一種程式設計方法, 函式有助於節省程式碼空間, 也可以利於保持程式一致性(修改一處, 所有代用都會跟著改變).

函式的屬性可以在定義函式時同時定義函式屬性, 也可以在函式宣告外定義函式屬性.
EXAMPLE: 可以通過句點識別符號和直接賦值的方法為一個函式新增屬性.

In [2]: foo()
in foo()

In [3]: foo.__dict__
Out[3]: {}

In [4]: foo.name = 'jmilkfan'

In [5]: foo()
in foo()

In [6]: foo.__dict__
Out[6
]: {'name': 'jmilkfan'} In [7]: foo.name Out[7]: 'jmilkfan'

還可以通過下面這種方式來定義函式屬性

In [15]: foo.__dict__['age'] = 24

In [16]: foo.__dict__
Out[16]: {'age': 24, 'name': 'jmilkfan'}

In [17]: foo.age
Out[17]: 24

NOTE: 這裡可以看出函式物件的 __dict__ 特殊屬性包含了函式物件的屬性和屬性值.

Python函式的建立

一般格式:

def functionName(parameters):
    """Document""" 
    函式體
    return [expression] 

建立一個自定義函式需要遵循下面幾個規則:
1.函式程式碼塊以def關鍵詞開始,接著函式識別符號、圓括號()和程式碼塊起始標識 : 號。
2.任何傳入引數和自變數都必須放在()。()內可以用於定義引數。當有多個引數時,使用逗號隔開。
3.函式體的第一行語句可以使用文件字串"""Document"""一般用作存放函式的使用說明。
在Python中,函式也是一個物件,( )中表示對可呼叫函式物件的一種排程,可呼叫的函式物件一定會繼承了可呼叫的方法call( )。這也是用來檢查一個函式可否能夠被呼叫的依據。

callable(functionName)

若輸出為True,表示物件functionName為可呼叫函式物件,其繼承了call()方法,是一個函式。

In [5]: callable(sys.stdout.write)
Out[5]: True

函式的引數

Python有下面幾種函式引數型別:
1.必備引數
2.命名引數
3.預設引數
4.不定長引數
5.匿名引數

必備引數

必備引數是在自定義函式時,最常見的引數型別。必備引數的使用有下面幾點要求:
1.在呼叫函式時,以形參與實參對應的順序來確定形參的值並傳入函式。
2.呼叫此函式時,傳遞的實參的數量必須與宣告時定義的形參的數量一致。

In [19]: %pycat func1Test.py
#!/usr/bin/env python
#Filename:func1Test.py

def DoubleCheckInput(input1,input2):
    if input1 == input2:
        print "Your enter the %s" % input1
    else:print "Two input iis not consistent!"
    return "Done"

if __name__ == '__main__':
    enter1 = raw_input("Ple enter your name:")
    enter2 = raw_input("ple enter your name again:")
    DoubleCheckInput(enter1,enter2)

In [20]: run func1Test.py
Ple enter your name:Jmilk
ple enter your name again:Jmilk
Your enter the Jmilk

注意:當傳遞的引數數目不一致時,會觸發下面的異常:

In [23]: %pycat func1Test.py
#!/usr/bin/env python
#Filename:func1Test.py

def DoubleCheckInput(input1,input2):
    if input1 == input2:
        print "Your enter the %s" % input1
    else:print "Two input iis not consistent!"
    return "Done"

if __name__ == '__main__':
    enter1 = raw_input("Ple enter your name:")
    enter2 = raw_input("ple enter your name again:")
    DoubleCheckInput(enter1)

In [24]: run func1Test.py
Ple enter your name:Jmilk
ple enter your name again:Jmilk
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/usr/local/src/pyScript/func1Test.py in <module>()
     11     enter1 = raw_input("Ple enter your name:")
     12     enter2 = raw_input("ple enter your name again:")
---> 13     DoubleCheckInput(enter1)

TypeError: DoubleCheckInput() takes exactly 2 arguments (1 given)

預設引數

預設引數既預設引數。顧名思義,預設引數可以實現在呼叫函式卻沒有指定實參時,可以為形引數提供一個預設值。相對的,當預設引數對應的實參有傳遞值時,預設引數的預設值會被覆蓋。從而實參的數目未必一定會與形參的數目一致,因為使用了預設引數。格式:

def functionName([para1,...,]paras=defaultValues)
In [46]: %pycat func1Test.py
#!/usr/bin/env python
#Filename:func1Test.py
import sys
def DoubleCheckInput(input1,input2='Jmilk'):
    if input1 == input2:
        print "Your enter the %s" % input1
    else:print "Two input iis not consistent!"
    return "Done"

if __name__ == '__main__':
    enter1 = raw_input("Ple enter your name:")
#    enter2 = raw_input("ple enter your name again:")
    DoubleCheckInput(enter1)

In [47]: run func1Test.py
Ple enter your name:Jmilk
Your enter the Jmilk

注意:即便預設引數會為形參提供一個預設的值,但是在Python的編譯過程的語法檢查時,引數的匹配依舊是按照順序傳遞。所以一般而言,當你希望使用預設引數的值的時候,需要在定義函式的引數列表時,預設引數一定要置於必備引數和命名引數之後。避免在引數傳遞的時候,實參在將引數列表前端的預設引數覆蓋後,卻沒有實參傳遞給必備引數。下面是一個例子:

In [56]: %pycat func1Test.py
#!/usr/bin/env python
#Filename:func1Test.py
import sys
def DoubleCheckInput(input1='Jmilk',input2):
    if input1 == input2:
        print "Your enter the %s" % input1
    else:print "Two input iis not consistent!"
    return "Done"

if __name__ == '__main__':
#    enter1 = raw_input("Ple enter your name:")
    enter2 = raw_input("ple enter your name again:")
    DoubleCheckInput(enter2)

In [57]: run func1Test.py
  File "/usr/local/src/pyScript/func1Test.py", line 4
    def DoubleCheckInput(input1='Jmilk',input2):
SyntaxError: non-default argument follows default argument

語法錯誤:非預設引數不能在預設引數之後

命名引數

命名引數也稱之為關鍵字引數, 其僅針對函式的呼叫, 讓呼叫者通過函式呼叫中的引數名字賦值來將實參對映到形參, 有下面幾個要點:
1.呼叫時,用賦值的方式來為函式傳入引數,呼叫格式:

functionName(formPara1=actualPara1[,...])

2.可以以任意順序來指定引數

In [40]: %pycat func1Test.py
#!/usr/bin/env python
#Filename:func1Test.py
import sys
def DoubleCheckInput(input1,input2):
    if input1 == input2:
        print "Your enter the %s" % input1
    else:print "Two input iis not consistent!"
    return "Done"

if __name__ == '__main__':
    enter1 = raw_input("Ple enter your name:")
    enter2 = raw_input("ple enter your name again:")
    DoubleCheckInput(input2=enter2,input1=enter1)

In [41]: run func1Test.py
Ple enter your name:Jmilk
ple enter your name again:Jmilk
Your enter the Jmilk

下面舉一個命名引數和預設引數一結合使用的例子:

In [7]: %pycat func1Test.py
#!/usr/bin/env python
#Filename:func1Test.py
import sys
def printYourEnter(input1,input2='Jmilk',input3='23'):
    print input1
    print input2
    print input3

if __name__ == '__main__':
    enter1 = raw_input("Ple enter your living cities:")
    enter2 = raw_input("ple enter your name:")
    enter3 = raw_input("Ple enter your age:")
    printYourEnter(enter1)

In [8]: run func1Test.py
Ple enter your living cities:BJ
ple enter your name:
Ple enter your age:
BJ
Jmilk
23

在上面的例子中使用了預設引數,當沒有實參傳遞到函式的時候,形參會使用預設值。但在有些情況中,我們希望可以重新指定形參input3的值,而且希望繼續使用input2的預設值。在大多數程式語言中,想要實現上面的要求,必須要使用3個引數來呼叫函式,即必須重新指定input2的值。但是在Python中因為命名引數型別,所以我們可以突破引數順序的限制。將上面的例子修改為:

In [13]: %pycat func1Test.py
#!/usr/bin/env python
#Filename:func1Test.py
import sys
def printYourEnter(input1,input2='Jmilk',input3='23'):
    print input1
    print input2
    print input3

if __name__ == '__main__':
    enter1 = raw_input("Ple enter your living cities:")
    enter2 = raw_input("ple enter your name:")
    enter3 = raw_input("Ple enter your age:")
    printYourEnter(input1=enter1,input3=enter3)

In [14]: run func1Test.py
Ple enter your living cities:BJ
ple enter your name:
Ple enter your age:24
BJ
Jmilk
24

可以看見,當我沒有傳遞input2,僅僅是傳遞了input1、input3,而input2還是使用了預設值,而不會因為沒有指定命名引數導致input2被覆蓋而input3使用預設值的情況。但是要注意的一點是,即便結合使用了命名引數和預設引數,仍然無法改變non-default parameter not follows the default parameter的語法規則。

不定長引數

不定長引數也稱之為引數組或冗餘引數, 當你希望一個函式可以接受處理比當初宣告定義函式時更多的引數,可以使用不定長引數。即,實現了函式的引數冗餘。當傳遞的實引數目比函式的形參更多時,一般會報錯。但函式中的不定長引數可以用來吸收多餘的引數。注意:不定長引數僅允許放到函式形參列表的最後。格式:

def functionName([para1,para2,...,]*indefinitePara):

def functionName([para1,para2,...,]**indefinitePara):

def functionName([para1,para2,...,]*indefinitePara1, **indefinitePara2):

其中的 “*” 標識了不定長引數為 (非關鍵字引數) 會儲存所有非關鍵字的冗餘引數(沒有在實參列表內指定關鍵字,EG. name='Jmilkfan'),並將冗餘的引數儲存為Tuple型別物件, 也可以向該形參傳遞一個 Tuple 型別的引數組, 兩者的效果是一樣的. 只是前者在函式內處理成 Tuple 的形式, 後者在呼叫之前就已經整理成 Tuple 了
Example1:

In [23]: %pycat func1Test.py
#!/usr/bin/env python
#Filename:func1Test.py
import sys
def printYourEnter(input1,*indePara):
    print input1
    print indePara
    for var in indePara:
        print var

if __name__ == '__main__':
    enter1 = raw_input("Ple enter your living cities:")
    enter2 = raw_input("ple enter your name:")
    enter3 = raw_input("Ple enter your age:")
    printYourEnter(enter1,enter2,enter3)

In [24]: run func1Test.py
Ple enter your living cities:BJ
ple enter your name:Jmilk
Ple enter your age:23
BJ
('Jmilk', '23')
Jmilk
23

上面例子的函式中使用了不定長引數*indePara,來存放enter2多餘的enter3兩個實參。並且是以元組的資料型別來儲存。
Example2:
而 “**” 表示為一個 (關鍵字引數),以字典的資料型別來儲存冗餘實參, 也可以向該形參傳遞一個 Dict 型別的實參。

In [4]: %pycat func1Test.py
#!/usr/bin/env python
#Filename:func1Test.py
def printYourEnter(input1,**indePara):
    print input1
    print indePara

if __name__ == '__main__':
    enter1 = raw_input("Ple enter your living cities:")
    enter2 = raw_input("ple enter your name:")
    enter3 = int(raw_input("Ple enter your age:"))
    printYourEnter(enter1,name=enter2,age=enter3)

In [5]: run func1Test.py
Ple enter your living cities:BJ
ple enter your name:Jmilk
Ple enter your age:23
BJ
{'age': 23, 'name': 'Jmilk'}

在呼叫函式時,使用key=value的對映關係元素,傳遞實參。本質上是傳遞了key的引用和value的資料兩個值,並且傳遞到函式中的**indePara字典型別。要注意這與命名引數的區別,命名引數是為了可以亂序傳遞實參,而不定長引數是為了將多餘的實參儲存在函式中的Tuple或Dictionary型別物件中。
注意:以Tuple或Dic作為形參吸收實按冗餘時,若實參中均沒有多餘的元素或沒有對映關係元素時,會自動的為函式中的Tuple或Dic型別物件填入空值,避免報錯。

In [9]: %pycat test.py
#!/usr/bin/env python
def test(x,*args,**kwargs):
    print x
    print args
    print kwargs
test(1)

In [10]: run test.py
1
()
{}

還需要注意的是:實參的對映關係元素中的key,不可以與已經存在的普通形參同名。否則會報錯

In [13]: %pycat test.py
#!/usr/bin/env python
def test(x,**kwargs):
    print x
    print kwargs

test(1,x=1,y=2)

In [14]: run test.py
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/usr/local/src/pyScript/test.py in <module>()
      4     print kwargs
      5 
----> 6 test(1,x=1,y=2)

TypeError: test() got multiple values for keyword argument 'x'

主要是避免命名引數和不定長引數的衝突。

匿名引數

匿名函式,即lambda函式。此函式能夠快速的創建出單行最小行函式並用於任何需要函式的地方,使用匿名引數有下面幾個要點:
1.無須使用def關鍵字來進行宣告。
2.lambda函式可以接受任何數量的實參,但只會且僅返回一個表示式的值。
3.lambda函式不能包含有命令和多個表示式。
4.不能直接呼叫print語句,因為lambda函式是單行函式且此行只能為表示式。
5.lambda函式擁有自己的名稱空間,不能訪問除自身引數列表之外的全域性名稱空間內的引數。
6.lambda最大的優勢在於,可以不佔用棧記憶體從而增加執行效率。
lambda函式的定義格式:

functionName = lambda [arg1[,arg2,...]]:expression

呼叫格式:

functionName(arg1[,arg2,..])

Example:

In [76]: %pycat lamTest.py
#!/usr/bin/env python
#Filename:lamTest.py
n = lambda x,y:x*y
print type(n)
print type(n(2,3))
print n(2,3)

In [77]: run lamTest.py
<type 'function'>
<type 'int'>
6

lambda在定義時會返回一個需要接收的函式物件,在呼叫後會返回一個型別物件。

偏函式的應用

偏函式的概念:就是將函數語言程式設計、預設引數和冗餘引數結合在一起,將任意數量(順序)的引數轉化為另一個帶有剩餘引數的函式物件,從而實現擷取函式功能(偏向)的效果。
NOTE:偏函式需要 functools Module 的支援。
EXAMPLE 1:將二進位制數轉化為十進位制

In [1]: int('10101010', base=2)
Out[1]: 170

一般而言我們會使用上述的方式來執行,但有些時候會覺得能不能將 int() 函式的 base 引數臨時的設定成預設引數呢?這樣的話就可以獲得一個專門為了實現 *將二進位制數轉化為十進位制
* 功能的函數了。下面就是偏函式其中一個小的應用例項:

In [2]: from functools import partial

In [3]: baseTwo = partial(int, base=2)

In [5]: baseTwo('10101010')
Out[5]: 170

將 int() 函式的 base 引數定義為預設引數,並返回給一個新的變數,這樣就像是生成了一個新的函式,EG.
baseTwo(x) == int(x, base=2)

除此之外,我們還可以利用函式屬性的特性來為這個 “新定義” 的函式編寫文件。

In [6]: baseTwo.__doc__ = 'Convert base 2 string to an int.'

In [8]: baseTwo.__doc__
Out[8]: 'Convert base 2 string to an int.'