Python高階函數和裝飾器

高階函數

  • First Class Object
    • 函數在Python中是一等公民
    • 函數也是對象,可調用(callable)的對象
    • 函數可以作為普通變量、參數、返回值等等
  • 高階函數
    • 數學概念y=g(f(x))
    • 在Python中,高階函數應該滿足下列至少一個條件
      1. 接受一個或者多個函數作為參數
      2. 輸出一個函數
  • 計數器
    def counter(base):
        def inc(step=1):
            base += step
            return base
        return inc
    
    分析:
        - 函數counter是一個高階函數,因為它返回了一個函數對象
        - 上述函數的問題在于內層函數使用外層函數的本地變量,但同時有修改它,所以調用是會報錯UnboundLocalError,想要更改只需要使用關鍵字nonlocal在要改變前聲明它是外層函數的變量
        - 調用應該使用counter(base)(step)
        - f1 =counter(5)和f1 = counter(5)是不相等的,因為上述調用是兩次調用,這樣他們的ID就是不一樣的,所以不相等

自定義sort函數

  • 排序問題:仿照內建函數sorted,自行實現一個sort函數(不使用內建函數),能夠為列表元素排序
  • 思路
    • 內建函數sorted是返回一個新的列表,可以設置升序或降序,可以設置一個排序函數。自定義的函數也應該有類似功能
    • 新建一個列表,遍歷原列表,和新列表的值依次比較決定如何插入到新列表中
  • 函數實現1。判斷以下代碼是如何排序,還能優化么?
    def sort(iterable):
        ret = []
        for x in iterable:
            for i,y in enumerate(ret):
                if x > y:       #找到大的就插入。如果換成x<y,函數就是另一種排序
                    ret.insert(i,x)     #降序
                    break
        else:       #不大于說明是最小的,尾部追加
            ret.append(x)
        return ret 
    
    print(sort([1,7,2,9,3,6,8,4,5]))
  • sort函數實現2。用一個參數控制順序
    def sort(iterable,reverse=False):
        ret = []
        for x in iterable:
            for i,y in enumerate(ret);
                flag = x > y if reverse else x < y
                if flag:
                    ret.insert(i,x)
                    break
            else:
                ret.append(x)
        return ret
        
    print(sort([1,7,2,9,3,6,8,4,5]))
  • sort函數實現3。
    def sort iterable,fn=lambda a,b:a>b):
        ret = []
        for x in iterable:
            for i,y in enumerate(ret);
                if fn(i,x):     #函數返回值是bool型
                    ret.insert(i,x)
                    break
            else:
                ret.append(x)
        return ret
    
    print(sort([1,7,2,9,3,6,8,4,5]))

內建函數-高階函數

  • sorted(iterable[,key][,reverse])排序
    1. 返回一個新列表1,對一個可迭代對象的所有元素排序,排序規則為可以定義的函數,reverse表示是否排序翻轉
    2. sorted(lst,key=lambda x:6-x) #返回新列表
    3. list.sort(key=lambda x:6-x) #就地修改
  • filter(function,iterable)
    1. 過濾可迭代對象的元素,返回一個迭代器
    2. function是一個具有一個參數的函數,返回bool
    3. 例如,過濾除被3整除的數字:list(filter(lambda x:x%3 == 0,[1,9,55,150,-3,78,28,123]))
  • map(function,*iterables) –> map object
    1. 對多個可迭代對象的元素按照指定的函數進行映射,返回一個迭代器
    2. 例子:
      • list(map(lambda x:2*x+1,range(5)))
      • dict(map(lambda x:(X%5,x),range(500)))

柯里化

  • 指的是將原來接受兩個參數的函數變成新的接受一個參數的函數的過程。新的函數返回一個以原有第二個參數為參數的函數
  • z = f(x,y)轉換成z = f(x)(y)的形式
  • 舉例
    def add(x,y):
        return x+y
    
    轉換如下:
    def add(x):
        def _add(y):
            return x+y
        return _add
        
    add(5)(6)
    
    通過嵌套函數就可以吧函數轉換稱柯里化函數

裝飾器

  • 需求
    • 一個加法函數,想要加強它的功能,能夠輸出被調用過以及調用的參數信息
      def add(x,y):
          return x+y
      
      增加信息輸出功能
      def add(x,y):
          print('call add,{}+{}'.format(x,y))
          return x+y
    • 上面的加法函數是完成了需求,但是有以下缺點:
      1. 打印語句的耦合太高
      2. 加法函數屬于業務功能,而輸出信息的功能,屬于非業務功能代碼,不應該放在業務函數內部
  • 推演過程
    首先將業務函數和增強功能分開,但發現fn函數調用傳參是個問題
    def add(x,y):
        return x + y
    
    def logger(fn):
        print('begin')
        x = fn(4,5)
        print('end')
        return x
    
    print(logger(add))
    
    解決傳參的問題
    def add(x,y):
        return x + y
    
    def logger(fn,*args,**kwargs):    #這里*args和**kwargs代表可變參數
        print('begin')
        x = fn(*args,**kwargs)      #*args,**kwargs在這表示參數解構
        print('end')
        return x
    
    print(logger(add,5,y=50))
    
    柯里化
    def add(x,y):
        return x + y
    
    def logger(fn):
        def _logger(*args,**kwargs):
            print('begin')
            x = fn(*args,**kwargs)
            print('end')
            return x
        return _logger
    
    print(logger(add)(5,y=50))  
    #最后也可以這么寫
    # add = logger(add)
    # print(add(x=5,y=10))
  • 裝飾器語法
def logger(fn):
    def _logger(*args,**kwargs):
        print('begin')
        x = fn(*args,**kwargs)
        print('end')
        return x
    return _logger
    
@logger #等價于add= logger(add),這就是裝飾器語法
def add(x,y):
    return x+y
    
print(45,50)
  • 上面的語法是無參裝飾器
    1. 它是一個函數
    2. 函數作為他的形參
    3. 返回值也是一個函數
    4. 可以使用@functionname方式,簡化調用
  • 裝飾器是高階函數,但裝飾器對傳入函數的功能的裝飾(功能增強)

文檔字符串

  • Python中文檔字符串Documentation Strings
  • 在函數體語句塊的第一行,且習慣是多行的文本,所以多使用三引號
  • 慣例是首字母大寫,第遺憾寫概述,空遺憾,第三行寫詳細描述
  • 可以是使用特殊屬性__doc__訪問這個文檔
def add(x,y):
    '''This is a function of addition'''
    a = x+y
    return a

print('name={}\ndoc={}'.format(add.__name__,add.__doc__))
print(help(add))

使用裝飾器的副作用

  • 使用裝飾器后原函數對象的屬性都被替換了,而使用裝飾器,希望查看原函數的屬性,如何解決
    1. 可以自己定義一個函數將被包裝函數的屬性覆蓋掉包裝函數
    def copy_properties(src,dst):   #自定義復制屬性的函數
        dst.__name__ = src.__name__
        dst.__doc__ = src.__doc__
        
    
    def logger(fn):
        def _logger(*args,**kwargs):
            print('begin')
            x = fn(*args,**kwargs)
            print('end')
            return x
        copy_properties(fn,_logger) #函數調用,復制被包裝函數屬性覆蓋包裝函數屬性
        return _logger
    
    @logger 
    def add(x,y):
        return x+y
    
    print(45,50)
    1. 凡是被裝飾的函數都需要賦值這些屬性,這個函數很通用
    2. 可以將被復制屬性的函數構建成裝飾器函數,帶參裝飾器
    def copy_properties(src):   #柯里化
        def _copy(dst):
            dst.__name__ = src.__name__
            dst.__doc__ = src.__doc__
            return dst
        return _copy
    
    def logger(fn):
        @copy_properties(fn)    #_logger = copy_properties(fn)(_logger)
        def _logger(*args,**kwargs):
            print('begin')
            x = fn(*args,**kwargs)
            print('end')
            return x
        
        return _logger
    
    @logger 
    def add(x,y):
        """this is """        
        return x+y
    
    print(45,50)

帶參裝飾器

  • 它是一個函數
  • 函數作為他的形參
  • 返回值是一個不帶參的裝飾器函數
  • 使用@functionname(參數列表)方式調用
  • 可以看作在裝飾器外層又加了一層函數
def logger(duration):
    def _logger(fn):
        def wrapper(*args,**kwargs):
            start = datetime.datetime.now()
            ret = fn(*args,**kwargs)
            delta =(datetime.datetime.now()-start).total_seconds()
            print('so slow') if delta >duration else print('so fast')
            return ret
        return wrapper
    return _logger

@logger(5)
def add(x,y):
    time.sleep(3)
    return x+y
print(add(5,6))
  • 將記錄的功能提取出來,就可以通過外部提供的哈數來靈活的控制輸出
def logger(duration,func=lambda name,duration:print('{}took{}s'.format(name,duration))):
    def _logger(fn):
        def wrapper(*args,**kwargs):
            start = datetime.datetime.now()
            ret = fn(*args,**kwargs)
            delta =(datetime.datetime.now()-start).total_seconds()
            if delta > duration:
                func(fn.__name__,duration)
            print('so slow') if delta >duration else print('so fast')
            return ret
        return wrapper
    return _logger

functools模塊

  • functools.update_wrapper(wrapper,wrapped,assigned=WRAPPER_ASSIGNMENTS,updated=WRAPPER_UPDATES)
    • 類似copy_properties功能
    • wrapper包裝函數,wrapped被包裝函數
    • 元組WRAPPER_ASSIGNMENTS中是要被覆蓋的屬性
    • 元組WRAPPER_UPDATES中是要被更新的屬性,__dict__屬性字典
    • 增加一個__wrapped__屬性,保留著wrapped函數
  • functools.wraps(wrapped,assigned=WRAPPER_ASSIGNMENTS,updated=WRAPPER_UPDATES)和上個方法用法基本相同

本文來自投稿,不代表Linux運維部落立場,如若轉載,請注明出處:http://www.www58058.com/88009

(0)
KX_ilKX_il
上一篇 2017-10-22 23:25
下一篇 2017-10-23 09:08

相關推薦

  • lvs-keepalived

    keepalived: vrrp協議:Virtual Redundant Routing Protocol        術語:        虛擬路由器:Virtual Router &n…

    Linux干貨 2017-05-13
  • 創建私有CA及dropbear的編譯安裝

    使用openssl創建私有CA 配置文件/etc/pki/tls/openssl.cnf certs           = $dir/certs         &nb…

    Linux干貨 2016-09-26
  • Linux下用戶屬性的更改

      Linux下更改用戶的屬性一般使用usermod命令   基本格式如下:            Usermod[options]  login      [options]中具體參數如下:    -u  U…

    Linux干貨 2017-05-30
  • Linux初識

    Linux 初識 計算機包含 CPU :運算器 、控制器、寄存器、緩存(加速) 存儲器:內存,RAM(Random Access Memory) Input:下指令,提供數據 Output:輸出數據加工的結果。 馮諾依曼體系 紙帶打孔 磁帶機 批處理 鍵盤、顯示器 多任務Multi task 虛擬機軟件 VMware workstation 12 PRO O…

    2017-07-13
  • shell進階之循環

    循環執行,將某代碼段重復運行多次

    重復運行多少次:

    循環次數事先已知

    循環次數事先未知

    有進入條件和退出條件

    for, while, until

    Linux干貨 2017-12-24
  • linux中的權限和ACL

    linux中權限詳解和ACL

    2017-11-18
欧美性久久久久