Python裝飾器AOP 不定長參數 鴨子類型 重載(三)

來源:互聯網
上載者:User

標籤:false   執行個體化   函數   元組   順序   學習   tin   man   ini   

1 可變長參數與關鍵字參數

*args代表任意長度可變參數

**kwargs代表關鍵字參數

*args**kwargs只是為了方便並沒有強制使用它們.

預設參數即是調用該函數時,預設參數的值若未被傳入,則傳入預設預設的值

注意 : 須將所有帶有預設值的參數置於參數列表的末尾

def print_info(name, age = 18,gender = True )print_info("zhan", gender = False )def demo(num, *nums ,**nums )

當你不確定你的函數裡將要傳遞多少參數時你可以用*args.例如,它可以傳遞任意數量的參數:

>>> def print_everything(*args):        for count, thing in enumerate(args):...         print ‘{0}. {1}‘.format(count, thing)...>>> print_everything(‘apple‘, ‘banana‘, ‘cabbage‘)0. apple1. banana2. cabbage

相似的,**kwargs允許你使用沒有事先定義的參數名:

>>> def table_things(**kwargs):...     for name, value in kwargs.items():...         print ‘{0} = {1}‘.format(name, value)...>>> table_things(apple = ‘fruit‘, cabbage = ‘vegetable‘)cabbage = vegetableapple = fruit

*args**kwargs可以同時在函數的定義中,但是*args必須在**kwargs前面.

當調用函數時你也可以用***文法.例如:

>>> def myPrint(a, b, c):...     print ‘a = {0}, b = {1}, c = {2}‘.format(a,b,c)...>>> mylist = [‘aardvark‘, ‘baboon‘, ‘cat‘]>>> myPrint(*mylist)a = aardvark, b = baboon, c = cat

就像你看到的一樣,它可以傳遞列表(或者元組)的每一項並把它們解包.注意必須與它們在函數裡的參數相吻合.當然,你也可以在函數定義或者函數調用時用*.

2 面向切面編程AOP和裝飾器

AOP實際就是面向切面編程,python實現方法是採用裝飾器模式.

? 裝飾器是一個很著名的設計模式,經常被用於有切面需求的情境,較為經典的有插入日誌、效能測試、交易處理等。裝飾器是解決這類問題的絕佳設計,有了裝飾器,我們就可以抽離出大量函數中與函數功能本身無關的雷同代碼並繼續重用。概括的講,裝飾器的作用就是為已經存在的對象添加額外的功能。

def makebold(fn):    def wrapped():        return "<b>" + fn() + "</b>"    return wrappeddef makeitalic(fn):    def wrapped():        return "<i>" + fn() + "</i>"    return wrapped@makebold@makeitalicdef hello():    return "hello world"print hello() ## returns <b><i>hello world</i></b>#通過兩個裝飾器實現對目標函數的封裝

理解裝飾器首先理解python函數同樣是對象,既然是對象就可以作為函數的傳回值,可以執行複製,添加屬性,作為函數參數傳遞,這些就是實現裝飾器的基礎

def bread(func):    def wrapper():        print "</‘‘‘‘‘‘\>"        func()        print "<\______/>"    return wrapperdef ingredients(func):    def wrapper():        print "#tomatoes#"        func()        print "~salad~"    return wrapperdef sandwich(food="--ham--"):    print foodsandwich()#outputs: --ham--sandwich = bread(ingredients(sandwich))  #裝飾器實際就是函數調用sandwich()#輸出:#</‘‘‘‘‘‘\># #tomatoes## --ham--# ~salad~#<\______/>

作為程式員必須學會偷懶,於是python採用@作為裝飾器文法糖,並學習一些進階用法:

@bread@ingredientsdef sandwich(food="--ham--"):    print foodsandwich()#輸出:  是不是覺得簡單很多啦!#</‘‘‘‘‘‘\># #tomatoes## --ham--# ~salad~#<\______/>#改變順序會有影響的,執行順序是先裡面@ingredients,在執行@bread

裝飾器的傳參

def a_decorator_passing_arguments(function_to_decorate):    def a_wrapper_accepting_arguments(arg1, arg2):        print "I got args! Look:", arg1, arg2        function_to_decorate(arg1, arg2)    return a_wrapper_accepting_arguments# 當你調用裝飾器返回的函數時,也就調用了封裝器,把參數傳入封裝器裡,# 它將把參數傳遞給被裝飾的函數裡.@a_decorator_passing_argumentsdef print_full_name(first_name, last_name):    print "My name is", first_name, last_nameprint_full_name("Peter", "Venkman")# 輸出:#I got args! Look: Peter Venkman#My name is Peter Venkman

裝飾器裝飾類方法

def method_friendly_decorator(method_to_decorate):    def wrapper(self, lie):        lie = lie - 3 # 女性福音 :-)        return method_to_decorate(self, lie)    return wrapperclass Lucy(object):    def __init__(self):        self.age = 32          @method_friendly_decorator#裝飾類方法    def sayYourAge(self, lie):        print "I am %s, what did you think?" % (self.age + lie)l = Lucy()l.sayYourAge(-3)#輸出: I am 26, what did you think?

裝飾器自己傳參數

def decorator_maker_with_arguments(decorator_arg1, decorator_arg2):    print("I make decorators! And I accept arguments:", decorator_arg1, decorator_arg2)    def my_decorator(func):        print("I am the decorator", decorator_arg1, decorator_arg2)        # 不要忘了裝飾器參數和函數參數!        def wrapped(function_arg1, function_arg2) :            print ("\t- from the decorator: {0} {1}\n"                  "\t- from the function call: {2} {3}\n"                  "Then I can pass them to the decorated function"                  .format(decorator_arg1, decorator_arg2,                          function_arg1, function_arg2))            return func(function_arg1, function_arg2)        return wrapped    return my_decorator@decorator_maker_with_arguments("Leonard", "Sheldon")def decorated_function_with_arguments(function_arg1, function_arg2):    print ("I am the decorated function and only knows about my arguments: {0}"           " {1}".format(function_arg1, function_arg2))decorated_function_with_arguments("Rajesh", "Howard") #調用函數#輸出:#I make decorators! And I accept arguments: Leonard Sheldon#I am the decorator. Leonard Sheldon#   - from the decorator: Leonard Sheldon#   - from the function call: Rajesh Howard#Then I can pass them to the decorated function#I am the decorated function and only knows about my arguments: Rajesh Howard
3 鴨子類型

“當看到一隻鳥走起來像鴨子、遊泳起來像鴨子、叫起來也像鴨子,那麼這隻鳥就可以被稱為鴨子。”

我們並不關心對象是什麼類型,到底是不是鴨子,只關心行為。

比如在python中,有很多file-like的東西,比如StringIO,GzipFile,socket。它們有很多相同的方法,我們把它們當作檔案使用。又比如list.extend()方法中,我們並不關心它的參數是不是list,只要它是可迭代的,所以它的參數可以是list/tuple/dict/字串/產生器等.

鴨子類型在動態語言中經常使用,非常靈活,使得python不像java那樣專門去弄一大堆的設計模式。

class duck():  def walk(self):    print(‘I walk like a duck‘)  def swim(self):    print(‘i swim like a duck‘) class person():  def walk(self):    print(‘this one walk like a duck‘)   def swim(self):    print(‘this man swim like a duck‘)    def watch_duck(animal): #定義一個函數,接受animal參數,需要具有walk swim兩項本領  animal.walk()  animal.swim() small_duck = duck()  #執行個體化鴨子watch_duck(small_duck) #能調用就認為是鴨子類型輸出 >> I walk like a ducki swim like a duck duck_like_man = person() #執行個體化人,但是人同樣有walk swim方法watch_duck(duck_like_man) #同樣被認為是鴨子類型輸出 >> this one walk like a duckthis man swim like a duck class Lame_Foot_Duck():  def swim(self):    print(‘i am lame but i can swim‘) lame_duck = Lame_Foot_Duck() #執行個體化蹩腳的鴨子,類下只具有swim方法watch_duck(lame_duck)  #雖然是鴨子,但是不被認為是鴨子類型 輸出 >>AttributeError: Lame_Foot_Duck instance has no attribute ‘walk‘
4 python中重載

函數重載主要是為瞭解決兩個問題。

  1. 可變參數類型
  2. 可變參數個數

一個對象的特徵不是由它的類型決定,而是通過對象中的方法決定,所以函數重載在動態語言中就顯得沒有意義.

另外,一個基本的設計原則是,僅僅當兩個函數除了參數類型和參數個數不同以外,其功能是完全相同的,此時才使用函數重載,如果兩個函數的功能其實不同,那麼不應當使用重載,而應當使用一個不同名的函數。

那麼對於情況 1 ,函數功能相同,但是參數類型不同,python 如何處理?答案是根本不需要處理,因為 python 可以接受任何類型的參數,如果函數的功能相同,那麼不同的參數類型在 python 中很可能是相同的代碼,沒有必要做成兩個不同函數。

那麼對於情況 2 ,函數功能相同,但參數個數不同,python 如何處理?大家知道,答案就是預設參數。對那些缺少的參數設定為預設參數即可解決問題。因為你假設函數功能相同,那麼那些缺少的參數終歸是需要用的。

好了,鑒於情況 1 跟 情況 2 都有瞭解決方案,python 自然就不需要函數重載了

class Write:    @staticmethod    def write(output,content):        #output對象只要實現write方法,不管接受的類型        output.write(content)#stringIO類型output = StringIO.StringIO()Write.write(output,‘helloworld‘)#file類型output = open(‘out.txt‘,‘w‘)Write.write(output,‘helloworld‘)

Python裝飾器AOP 不定長參數 鴨子類型 重載(三)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.