Python装饰器AOP 不定长参数 鸭子类型 重载(三)

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')
posted @ 2018-06-17 22:17  诚实善良小郎君  阅读(430)  评论(0编辑  收藏  举报