zoukankan      html  css  js  c++  java
  • Python装饰器的一点解读

      版权申明:本文为博主窗户(Colin Cai)原创,欢迎转帖。如要转贴,必须注明原文网址
    
      http://www.cnblogs.com/Colin-Cai/p/12977127.html 
    
      作者:窗户
    
      QQ/微信:6679072
    
      E-mail:6679072@qq.com

      理论上,函数是一等公民(first class function)的语言都可以使用函数式编程,从而利用算子(高阶函数)来做装饰器。

      装饰器一般是这样一个算子,它接受一个函数作为参数,返回另外一个函数。装饰器,顾名思义,就是把一个函数“装饰”一下,得到另外一个函数。为何要装饰一下呢?目的一般是可能设计上需要对函数做一些改装,比如原函数输出结果需要再加工加工,或者原函数的输入参数传入不一样,或者两者兼有之,等等。

      迭代是编程中常用的手段,它的计算方式表现为状态的不断变换,且状态的变换具有唯一性。

      比如我们使用Scheme来表示迭代。

    ;stat代表当前状态,next代表状态改变函数,final?代表判断状态是否终止
    (define (iterate-orgin stat next final?)
      (if (final? stat)
          stat
          (iterate-orgin (next stat) next final?)))
    
    ;将next函数和final?函数封成一个函数
    (define (iterate stat f-stat)
      (iterate-orgin stat (f-stat 'next) (f-stat 'final)))
    
    ;最终我们需要的迭代函数
    (define (it f-stat)
      (lambda (stat) (iterate stat f-stat)))

      

      以上构造出一个算子it,就是用来“装饰”迭代的函数。

      我们构造一个对list求和的迭代:

      可以每次把list的前面两个相加,比如对(1 2 3 4 5)求和,经过以下状态:

      (1 2 3 4 5)

      (3 3 4 5)

      (6 4 5)

      (10 5)

      (15)

      15

      得到最后结果15。

      代码可以如下:

    (define (make-sum-func sym)
     (if (eq? sym 'next);next函数
      (lambda (lst)
       (if (pair? lst)
        (if (null? (cdr lst))
         (car lst)
         (cons (+ (car lst) (cadr lst)) (cddr lst)))
        lst))
      (if (eq? sym 'final?);final?函数
       (lambda (lst) (not (pair? lst)))
       '())))

      然后测试一下((it make-sum-func) '(1 2 3 4 5)),得到最后结果15。

      上面两个函数写在一起,我们还可以再分离一下。

      

    ;定义一个打包函数
    (define (wrap-next-final next final?)
     (lambda (sym)
      (if (eq? sym 'next)
       next
       (if (eq? sym 'final?)
        final?
        '()))))
    
    ;下面next和final?两个函数可以分开写
    (define make-sum-next
     (lambda (lst)
      (if (pair? lst)
       (if (null? (cdr lst))
        (car lst)
        (cons (+ (car lst) (cadr lst)) (cddr lst)))
       lst)))
    
    (define make-sum-final?
     (lambda (lst) (not (pair? lst))))
    
    ;于是函数就可以下面这样表示
    (define make-sum-func (wrap-next-final make-sum-next make-sum-final?))

      总而言之,装饰器就是这样一类算子。

      Python也是这样,只是Python提供了@这样的语法,实际上是个语法糖,与其说是简写,倒是更像是个语法提醒这是一个装饰器。

      我们这次希望来显示一下mysym,还是用求和。

      先写一个简单的mysum函数用于求和: 

    def mysum(*args):
        ret = 0
        for i in args:
            ret += i
        return ret

      做一个算子,来扩充它的输入参数:

      这里需要用来判断一个对象是否是可迭代对象,

      from collections import Iterable

      然后,如果判断对象x是否是可迭代对象,只需要:

      isinstance(x, Iterable)

      算子如下:

    from collections import Iterable
    def args_expan(f):
        def f2(*args):
            lst = []
            for i in args:
                if isinstance(i, Iterable):
                    lst.append(f(*i))
                else:
                    lst.append(i)
            return f(*lst)
        return f2

      然后在mysum前添加装饰器标志

    @args_expan
    def mysum(*args):
        ret = 0
        for i in args:
            ret += i
        return ret

      测试如下:

    print(mysum(1,2,3,4,5))
    print(mysum((1,2,3,4,5)))
    print(mysum([1,2,3,4,5]))
    print(mysum(range(1,6)))
    print(mysum(map(lambda x:x+1, range(5))))
    print(mysum(filter(lambda x:x<6, range(10))))
    
    #构造了一个生成器
    def gen_range(a, b):
        while a < b:
            yield a
            a += 1
    
    print(mysum(
        filter(lambda x:x<6, range(10)), 
        6, 
        [7,8], 
        (9, 10), 
        map(lambda x:x+11, range(10)), 
        gen_range(21,101)))

      运行得到:

    15
    15
    15
    15
    15
    15
    5050

      终于看到,函数功能已被扩充。

      这个装饰器还可以装饰别的函数,比如乘积、统计等。

      从而,装饰器就是这样一个算子,一般用来改造函数的输入或输出,避免重复写代码。

  • 相关阅读:
    php八种常用函数
    已知二叉树的前序中序遍历,如何得到它的后序遍历?
    PTA_Have fun with numbers(C++)
    PTA_输入符号及符号个数打印沙漏(C++)
    Web安全之SQL注入
    南京邮电大学//bugkuCTF部分writeup
    修改或添加HTTP请求头
    第二次作业
    博客作业1
    linux python 串口
  • 原文地址:https://www.cnblogs.com/Colin-Cai/p/12977127.html
Copyright © 2011-2022 走看看