CrazyWing:Python自動化運維開發實戰 十4、Python函數

導語:

函數是組織好,可重複使用,用來實現單一或相關聯功能的代碼段,能提升應用的模塊性和代碼的重複利用率。
Python提供了許多內建函數,好比print()。
也能夠本身建立函數,被叫作用戶自定義函數。python

定義函數:

規則:express

函數代碼塊以 def 關鍵詞開頭,後接函數標識符名稱和圓括號()。
任何傳入參數和自變量必須放在圓括號中間。圓括號之間能夠用於定義參數。
函數的第一行語句能夠選擇性地使用文檔字符串—用於存放函數說明。
函數內容以冒號起始,而且縮進。
return [表達式] 結束函數,選擇性地返回一個值給調用方。不帶表達式的return至關於返回 None。

語法:vim

def functionname( parameters ):
       "函數_文檔字符串"
       function_suite
       return [expression]

例:
#!/usr/bin/python
def printme( str ): # 定義函數
"打印任何傳入的字符串"
print str
returnapp

調用函數

printme("我要調用用戶自定義函數!")
printme("再次調用同一函數")

以上實例輸出結果:
我要調用用戶自定義函數!
再次調用同一函數

按引用傳遞參數:

全部參數在Python裏都是按引用傳遞。若是你在函數裏修改了參數,那麼在調用這個函數的
函數裏,原始的參數也被改變了。

例:
#!/usr/bin/python
def changeme( mylist ):
"修改傳入的列表"
mylist.append([1,2,3,4]);
print "函數內取值: ", mylist
returnide

# 調用changeme函數
mylist = [10,20,30];
changeme( mylist );
print "函數外取值: ",mylist

傳入函數的和在末尾添加新內容的對象用的是同一個引用。故輸出結果以下:
函數內取值:  [10, 20, 30, [1, 2, 3, 4]]
函數外取值:  [10, 20, 30, [1, 2, 3, 4]]

文檔字符串

咱們在使用def 關鍵字定義一個函數時,其後必須跟有函數名和包括形式參數的圓括號。函數體的下一行開始,必須是縮進的。函數體的第一行能夠是字符串,這個字符串就是文檔字符串documentation string,一般也稱做:docstring函數

定義:

在函數體的第一行,可使用一對三引號(''')或者(""")來定義文檔字符串,文檔字符串一般第一行以大寫字母開頭,以句號 (.)結束,第二行是空行,第三行開始是詳細描述。強烈建議爲你重要的函數寫文檔字符串都遵循此慣例。 工具

做用:

文檔字符串是使用Python過程當中一個很重要的工具,他對程序文檔頗有幫助,使程序很容易理解。甚至當程序運行的時候,能夠從一個函數中返回文檔字符串。把函數當作一個對象來看,這更有助於咱們的理解,就至關於獲取一個對象的屬性(__doc__).ui

函數文檔字符串舉例:

#cat nester.py    這是一個模塊文件
#!/usr/bin/env python3
"這是模塊文檔字符串"
def print_list(name):
    '''這是函數文檔字符串'''
    for each_item in name:
        if isinstance(each_item,list):
            print_list(each_item)
        else:
            print each_item

#python         
>>> import nester           
>>> print nester.print_list.__doc__
這是函數文檔字符串
>>> help(nester)
Help on module nester:
NAME
    nester - 這是模塊文檔字符串
FILE
    /python/nester.py
FUNCTIONS
    print_list(name)
        這是函數文檔字符串
(END)

參數:

調用函數時可以使用的參數類型:
• 必備參數
• 關鍵字參數
• 默認參數
• 不定長參數debug

必備參數:

必備參數須以正確的順序傳入函數。調用時的數量必須和聲明時的同樣。
好比調用printme()函數,你必須傳入一個參數,否則會出現語法錯誤:
#!/usr/bin/python
def printme( str ):
print str;
return;日誌

#調用printme函數
printme();

輸出結果:
Traceback (most recent call last):
File "test.py", line 11, in <module>
printme();
TypeError: printme() takes exactly 1 argument (0 given)

關鍵字參數:

關鍵字參數和函數調用關係緊密,函數調用使用關鍵字參數來肯定傳入的參數值。
使用關鍵字參數容許函數調用時參數的順序與聲明時不一致,由於 Python 解釋器可以用參數名匹配
參數值。
如下實例在函數 printme() 調用時使用參數名:
#!/usr/bin/python3
def printme( str ):
print str;
return;

#調用printme函數
printme( str = "My string");

輸出結果: 
My string

下例能將關鍵字參數順序不重要展現得更清楚:
#!/usr/bin/python3
def printinfo( name, age ):
print "Name: ", name;
print "Age ", age;
return;

#調用printinfo函數
printinfo( age=50, name="miki" )

輸出結果:
Name:  miki
Age  50

缺省參數:

調用函數時,缺省參數的值若是沒有傳入,則被認爲是默認值。
下例會打印默認的age,若是age沒有被傳入:
#!/usr/bin/python3
def printinfo( name, age = 35 ):
print "Name: ", name;
print "Age ", age;
return;

#調用printinfo函數
printinfo( age=50, name="miki" );
printinfo( name="miki" )

輸出結果:
Name:  miki
Age  50
Name:  miki
Age  35

不定長參數:

若是須要一個函數能處理比當初聲明時更多的參數。這些參數叫作不定長參數,和上述2種參數不
同,聲明時不會命名。
語法:
def functionname([formal_args,] var_args_tuple ):
function_suite
return [expression]
#加了星號(
)的變量名會存放全部未命名的變量參數。

例1:
#!/usr/bin/python3
def printinfo( arg1, *vartuple ):
print "輸出: "
print arg1
for var in vartuple:
print var
return

# 調用printinfo 函數
printinfo( 10 );
printinfo( 70, 60, 50 )

輸出結果:
    輸出:
    10
    輸出:
    70
    60
    50

例2:
#vim multi.py
def multiarg(*args):
for i in args:
print i
return

def multiargs(*args):
    print args[2]
    return

print "第一個調用:"
multiarg('hello','lili','tom','wing')
print "第2個調用:"
multiargs('hello','lili','tom','wing')        

執行結果:       
    第一個調用:
    hello
    lili
    tom
    wing
    第2個調用:
    tom

*args和**kwargs

[wing@macserver ~]$ cat b.py
#!/usr/bin/env python
def foo(*args,**kwargs):
    print 'args = ',args
    print 'kwargs = ',kwargs
    print '-'*20
if __name__ == '__main__':
    foo(1,2,3,4)                         
    foo(a=1,b=2,c=3)
    foo(1,2,3,4,a=1,b=2,c=3)    #同時使用*args和**kwargs,必須*args參數列在前
    foo('a',1,None,a=1,b='2',c=3)
    #*args是一個tuple,**kwargs表示關鍵字參數,是一個dict

執行結果:
[wing@macserver ~]$ python b.py
args =  (1, 2, 3, 4)
kwargs =  {}
--------------------
args =  ()
kwargs =  {'a': 1, 'c': 3, 'b': 2}
--------------------
args =  (1, 2, 3, 4)
kwargs =  {'a': 1, 'c': 3, 'b': 2}
--------------------
args =  ('a', 1, None)
kwargs =  {'a': 1, 'c': 3, 'b': '2'}
--------------------

return 語句:

return [表達式] 用來退出函數,選擇性地向調用方返回一個表達式。
不帶參數值的return語句返回None
例:
#!/usr/bin/python3
def sum( arg1, arg2 ):
total = arg1 + arg2
return total

# 調用sum函數
print sum( 10, 20 )

輸出結果:
30

變量做用域:

一個程序全部的變量並非在任何位置均可以訪問的。訪問權限決定於這個變量是在哪裏賦值的。
變量的做用域決定了在哪一部分程序你能夠訪問哪一個特定的變量名稱。
調用函數時,全部在函數內聲明的變量名稱都將被加入到做用域中

兩種最基本的變量做用域:全局變量和局部變量

定義在函數內部的變量擁有一個局部做用域
定義在函數外的變量擁有全局做用域

局部變量只能在其被聲明的函數內部訪問
全局變量能夠在整個程序範圍內訪問

例:
#!/usr/bin/python
total = 0 # 這是一個全局變量
def sum( arg1, arg2 ):
total = arg1 + arg2 # total在這裏是局部變量.
print "函數內是局部變量 : ", total
return total;

#調用sum函數
sum( 10, 20 );
print "函數外是全局變量 : ",total 

輸出結果:
函數內是局部變量 :  30
函數外是全局變量 :  0

命名空間和做用域

變量:
是擁有匹配對象的名字(標識符)。

命名空間:
是一個包含了變量名稱們(鍵)和它們各自相應的對象們(值)的字典。

1.一個Python表達式能夠訪問局部命名空間和全局命名空間裏的變量。
2.若是一個局部變量和一個全局變量重名,則局部變量會覆蓋全局變量。
3.每一個函數都有本身的命名空間。類的方法的做用域規則和一般函數的同樣。
4.Python認爲任何在函數內賦值的變量都是局部的。所以,若是要給全局變量在一個函數裏賦
值,必須使用global語句。

global VarName表達式會告訴Python, VarName是一個全局變量,這樣Python就不會在
局部命名空間裏尋找這個變量了。

例如,在全局命名空間裏定義一個變量money,再在函數內給變量money賦值,而後Python
會假定money是一個局部變量。然而,咱們並無在訪問前聲明一個局部變量money,結
果就是會出現一個UnboundLocalError的錯誤。取消global語句的註釋就能解決這個問題。
#!/usr/bin/python
Money = 2000
def AddMoney():
#想改正代碼就取消如下注釋:
#global Money
Money = Money + 1

print Money
AddMoney()
print Money

globals()和locals()函數

根據調用地方的不一樣,globals()和locals()函數可被用來返回全局和局部命名空間裏的名字。

在函數內部調用locals():
返回的是全部能在該函數裏訪問的命名。

在函數內部調用globals():
返回的是全部在該函數裏能訪問的全局名字。

兩個函數的返回類型都是字典。因此名字們能用keys()函數摘取。

匿名函數與 lambda

python 使用 lambda 來建立匿名函數。
• 匿名是由於不須要以標準的方式來聲明,好比說, 使用 def 語句。
• 一個完整的 lambda「語句」表明了一個表達式,這個表達式的定義體必須和聲明放在同一行。
• lambda的主體是一個表達式,而不是一個代碼塊。僅僅能在lambda表達式中封裝有限的邏輯進去。
• lambda函數擁有本身的命名空間,且不能訪問自有參數列表以外或全局命名空間裏的參數。
• lambda函數看起來只能寫一行,卻不等同於C或C++的內聯函數,後者的目的是調用小函數時不佔用棧內存從而增長運行效率。

語法:
lambda [arg1 [,arg2,.....argn]]:expression
參數是可選的,若是使用參數,參數一般也是表達式的一部分。
例:
#!/usr/bin/python
sum = lambda arg1, arg2: arg1 + arg2

# 調用sum函數
print "相加後的值爲 : ", sum( 10, 20 )
print "相加後的值爲 : ", sum( 20, 20 )

輸出結果:
相加後的值爲 :  30
相加後的值爲 :  40

複習下單行語句:
def true():
return True
上面的函數沒有帶任何的參數而且老是返回 True。python 中單行函數能夠和標題寫在同一行。

重寫true()函數:
    def true(): return True

使用 lambda 的等價表達式(沒有參數,返回一個 True)爲:
lambda :True

In [4]:     a = lambda x, y=2: x + y
In [5]:     a(5)
Out[5]:   7

In [6]:     a(3,5)
Out[6]:   8

In [2]:     a=lambda *z:z
In [3]:     a(1,2,3,4)
Out[3]:   (1, 2, 3, 4)

函數不帶括號:

In [1]: def hello(a,b): #函數定義
...: print a + b
...:

In [2]: hello(1,2) #函數調用,帶括號表示告訴編譯器」執行這個函數「

In [6]: c=hello #不帶括號表示把函數賦給另外一個函數對象
In [8]: c(3,4)
7

遞歸函數

遞歸函數不須要任何修改就能夠處理任意深度的嵌套列表

#!/usr/bin/env python
name=['wing',['tom',['jim',['lilei','han×××']]]]
def print_list(name):
    for each_item in name:
        if isinstance(each_item,list):
            print_list(each_item)
        else:
            print each_item

print_list(name)
def print_list(list_name,level=0):
    for each_item in list_name:
        if isinstance(each_item,list):
            print_list(each_item,level+1)
        else:
            for tab_num in range(level):
                print "\t",
            print each_item

print_list(name)
題目:利用遞歸方法求5!。                     
程序分析:遞歸公式:fn=fn_1*4!
程序源代碼:
#!/usr/bin/python
# -*- coding: UTF-8 -*-

def fact(j):
    sum = 0
    if j == 0:
        sum = 1
    else:
        sum = j * fact(j - 1)
    return sum

for i in range(5):
    print '%d! = %d' % (i,fact(i))
以上實例輸出結果爲:
0! = 1
1! = 1
2! = 2
3! = 6
4! = 24

內建函數

Python中,按照對象是否可變,將類型分類爲:

不可變類型:
對象的內容不可以改變(not mutable),這些類型中主要有數值類型(整數,浮點數,複數),字符
串類型,元組等
可變類型:
對象的內容可以改變(mutable),主要有列表,字典

Python針對衆多的類型,提供了衆多的內建函數來處理(內建是相對於導入import來講的,後面學
習到包package時,將會介紹),

這些內建函數功用在於其每每可對多種類型對象進行相似的操做,即多種類型對象的共有的操做;
若是某種操做只對特殊的某一類對象可行,Python常將其設置爲該種類型的方法(method)

內建函數的查看
經過在python交互模式下,鍵入相應的命令便可查看當前python版本的一些內建函數
>>> dir(__builtins__)
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BufferError', 'BytesWarning', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'ReferenceError', 'RuntimeError', 'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '_', '__debug__', '__doc__', '__import__', '__name__', '__package__', 'abs', 'all', 'any', 'apply', 'basestring', 'bin', 'bool', 'buffer', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'cmp', 'coerce', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'execfile', 'exit', 'file', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'intern', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'long', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'raw_input', 'reduce', 'reload', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip']

也能夠經過以下方式查看:
    >>> import __builtin__
    >>>dir(__builtin__)

獲取內建函數幫助:

help()  
獲取幫助信息

其完整的通常使用形式爲:
help(module.class.function)

例子:
In [3]: import sys
In [4]: help(sys.getsizeof)

數值類型表示的內建函數

bin()     獲取一個整數(int類型或長整型),返回其2進制形式的字符串
oct()     獲取一個整數,返回其8進制形式的字符串
hex()    獲取一個整數,返回其16進制形式的字符串

對象生成的內建函數

int() 將數值或字符串轉換爲整數int,完整使用形式int(x,base),base用於指定進制
long() 將數值或字符串轉換爲整數long,完整使用形式long(x, base),base用於指定進制
float() 將數值或字符串轉換爲浮點數
complex()返回一個複數,完整使用形式 complex(real,imag)
str() 將所給對象轉換爲字符串,使用形式爲str(object)
list() 獲取對象,轉換爲列表, list(object)
dict() 獲取映射轉換爲字典,dict(mapping)
tuple() 獲取一個可迭代的對象,返回一個元組, tuple(iterable)
注:這裏留意dict()內建函數建立字典的不一樣方式

裝飾器Decorator

裝飾器是咱們如今碰到的難點之一,不大好理解,能夠簡單的認爲裝飾器就是爲了給函數添加額外的功能,而不用每次都手寫那個功能,只須要用"@裝飾器名稱"調用就能夠了

不使用裝飾器給函數添加一個額外的功能:
下例中是給foo函數添加日誌功能

#!/usr/bin/env python3
import logging
def foo():
        print "hello world"

def use_logging(func):
        logging.warn("%s is running" % func.__name__)
        func()

use_logging(foo)   #每次執行foo的時候都得調用use_logging函數

使用裝飾器:
@use_logging #裝飾器
foo()

簡陋寫法:由於不能給帶有參數的函數裝飾,因此簡陋
#!/usr/bin/env python
#coding=utf8
import logging

def use_logging(func):
    logging.warn("%s is running" % func.__name__)
    return func

@use_logging
def foo():
    print "hello world"

foo()

標準寫法:
    #!/usr/bin/env python
    #coding=utf8
    import logging

    def use_logging(func):
        def wrapper(*args,**kw):
            logging.warn("%s is running" % func.__name__)
            return func()
        return wrapper

    @use_logging
    def foo():
        print "hello world"

    foo()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
帶參數:
#!/usr/bin/env python
#coding=utf8
import logging

def use_logging(func):
    def wrapper(*args,**kw):   
        logging.warn("%s is running" % func.__name__)
        return func()               #會報錯
    return wrapper

@use_logging
def foo(arg1,arg2):
    print "arg1+arg2=%s" % (arg1+arg2)

foo(5,3)

正確寫法以下:
#!/usr/bin/env python
#coding=utf8
import logging

def use_logging(func):
    def wrapper(*args,**kw):      
        logging.warn("%s is running" % func.__name__)
        return func(*args,**kw)   
    return wrapper

@use_logging
def foo(*args,**kw):
    print "arg1+arg2=%s" % (args[0]+args[1])
    print "hello world"

foo(1,2)
由於裝飾器會先執行,在沒有wrapper函數的狀況下return func(*args,**kw)會出現args未定義錯誤
相關文章
相關標籤/搜索