1. 程式人生 > >Python裝飾器AOP 不定長參數 鴨子類型 重載(三)

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

大量 傳遞 items extend rgs gzip rap 遊泳 很多

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. apple
1. banana
2. 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 = vegetable apple = 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 wrapped

def makeitalic(fn):
    def wrapped():
        return "<i>" + fn() + "</i>"
    return wrapped

@makebold
@makeitalic
def hello():
    return "hello world"

print hello() ## returns <b><i>hello world</i></b>
#通過兩個裝飾器實現對目標函數的包裝

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

def bread(func):
    def wrapper():
        print "</‘‘‘‘‘‘\>"
        func()
        print "<\______/>"
    return wrapper

def ingredients(func):
    def wrapper():
        print "#tomatoes#"
        func()
        print "~salad~"
    return wrapper

def sandwich(food="--ham--"):
    print food

sandwich()
#outputs: --ham--
sandwich = bread(ingredients(sandwich))  #裝飾器實際就是函數調用
sandwich()

#輸出:
#</‘‘‘‘‘‘\>
# #tomatoes#
# --ham--
# ~salad~
#<\______/>

作為程序員必須學會偷懶,於是python采用@作為裝飾器語法糖,並學習一些高級用法:

@bread
@ingredients
def sandwich(food="--ham--"):
    print food

sandwich()
#輸出:  是不是覺得簡單很多啦!
#</‘‘‘‘‘‘\>
# #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_arguments
def print_full_name(first_name, last_name):
    print "My name is", first_name, last_name

print_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 wrapper

class 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 duck
i swim like a duck
 
duck_like_man = person() #實例化人,但是人同樣有walk swim方法
watch_duck(duck_like_man) #同樣被認為是鴨子類型
輸出 >> 
this one walk like a duck
this 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 不定長參數 鴨子類型 重載(三)