《深入理解Python特性》學習筆記之高效的函數


1 函數是 Python 的對象

1 Python 中一切皆爲對象,函數也不例外。可以將函數分配給變量或存儲在數據結構中。
2 作爲頭等對象,函數還可以被傳遞給其他函數或作爲其他函數的返回值。
3 頭等函數的特性可以用來抽象並傳遞程序中的行爲。
4 函數可以嵌套,並且可以捕獲並攜帶父函數的一些狀態。具有這種行爲的函數稱爲閉包
5 對象可以被設置爲可調用的,因此很多情況下可以將其作爲函數對待。

1.1 函數是對象

def yell(text):
    return text.upper() + '...'


bark = yell
print(bark)             # <function yell at 0x0000027E7DDAD1E0>
print(yell)             # <function yell at 0x0000027E7DDAD1E0>
demo1_1 = bark("Hello world | function test demo 1")
print(demo1_1)          # HELLO WORLD | FUNCTION TEST DEMO 1...

del yell
# yell("Hello")         # NameError: name 'yell' is not defined

hi = bark("Hello")
print(hi)               # HELLO...
print(bark.__name__)    # yell

1.2 函數可傳遞給其他函數

def greet(func):
    greeting = func('Hi, I am a Python program')
    print(greeting)


print(greet(bark))      # HI, I AM A PYTHON PROGRAM...

Python 中具有代表性的高階函數是內置的 map 函數。map 接受一個函數對象和一個可迭代對象,然後在可迭代對象中的每個元素上調用該函數來生成結果。

下面通過將 bark 函數映射到多個問候語中來格式化字符串:

print(list(map(bark, ['hello', 'hey', 'hi'])))   # ['HELLO...', 'HEY...', 'HI...']

1.3 嵌套函數

def speak(text):
    def whisper(t):
        return t.upper() + '!!!'
    return whisper(text)


print(speak('Hello, World'))        # HELLO, WORLD!!!

1.4 閉包示例

def get_speak_func(text, volume):
    # 內部函數 whisper 和 _yell,注意其中並沒有 text 參數
    def whisper():
        return text.lower() + '...'

    def _yell():
        return text.upper() + '!'
    if volume > 0.5:
        return _yell
    else:
        return whisper


hello = get_speak_func('Hello, World', 0.7)()
print(hello)        # Output: 'HELLO, WORLD!'

2 lambda 是單表達式函數

簡單示例

>>> add = lambda x, y: x + y
>>> add(5, 3)
8

2.1 函數表達式

expert = (lambda x, y: x + y)(5, 3)
print(expert)       # 8

2.2 lambda 使用場景:排序

tuples = [(1, 'd'), (2, 'b'), (4, 'a'), (3, 'c')]
tuples = sorted(tuples, key=lambda x: x[1], reverse=False)
print(tuples)   # [(4, 'a'), (2, 'b'), (3, 'c'), (1, 'd')]

將 lambda 和 map()filter()結合起來構建複雜的表達式也很難讓人理解,
此時用列表解析式或生成器表達式通常會清晰不少:

# 有害:
>>> list(filter(lambda x: x % 2 == 0, range(16)))
[0, 2, 4, 6, 8, 10, 12, 14]

# 清晰:
>>> [x for x in range(16) if x % 2 == 0]
[0, 2, 4, 6, 8, 10, 12, 14]

3 裝飾器的力量

裝飾器的一大用途是將通用的功能應用到現有的類或函數的行爲上,這些功能包括:
1 日誌(logging
2 訪問控制和授權
3 衡量函數,如執行時間
4 限制請求速率(rate-limiting
5 緩存,等等

def trace(func):
    def wrapper(*args, **kwargs):
        print(f'TRACE: calling {func.__name__}() '
              f'with {args}, {kwargs}')
        original_result = func(*args, **kwargs)

        print(f'TRACE: {func.__name__}() '
              f'returned {original_result!r}')
        return original_result
    return wrapper


@trace
def say(name, line):
    return f'{name}: {line}'


text = say('Jane', 'Hello, World')
print(text)
# TRACE: calling say() with ('Jane', 'Hello, World'), {}
# TRACE: say() returned 'Jane: Hello, World'
# Jane: Hello, World

Python 裝飾器寫日誌

示例如下

from functools import wraps


def log(func):
    @wraps(func)
    def wrapper(*args, **kw):
        if func.__name__ == "debug":
            msg = "debug {}".format(args[0])
        elif func.__name__ == "info":
            msg = "info {}".format(args[0])
        else:
            msg = "unknown {}".format(args[0])
        return func(msg, **kw)
    return wrapper


@log
def debug(msg):
    print(msg)


@log
def info(msg):
    print(msg)


if __name__ == "__main__":
    debug("bug!")
    info("msg.")
    
# 輸出結果如下:    
# debug bug!
# info msg.

4 有趣的 *args**kwargs

*args**kwargs 用於在 Python 中編寫變長參數的函數。
*args 收集額外的位置參數組成元組。**kwargs 收集額外的關鍵字參數組成字典。

def foo(required, *args, **kwargs):
    print(required)
    if args:
        print(args)
    if kwargs:
        print(kwargs)


# print(foo())          # TypeError: foo() missing 1 required positional argument: 'required'

foo('hello')            # hello

foo('hello', 1, 2, 3)
# hello
# (1, 2, 3)

foo('hello', 1, 2, 3, key1='value', key2=999)
# hello
# (1, 2, 3)
# {'key1': 'value', 'key2': 999}

5 函數參數解包

*** 操作符有一個非常棒但有點神祕的功能,那就是用來從序列和字典中 “解包” 函數參數。
高效使用參數解包有助於爲模塊和函數編寫更靈活的接口。

def print_vector(x, y, z):
    print('<%s, %s, %s>' % (x, y, z))


express = (x * x for x in range(3))
print_vector(*express)      # <0, 1, 4>

dict_vec = {'y': 0, 'z': 1, 'x': 1}
print_vector(**dict_vec)    # <1, 0, 1>
print_vector(*dict_vec)     # <y, z, x>

6 返回空值

Python 在所有函數的末尾添加了隱式的 return None 語句。因此,如果函數沒有指定返回值,默認情況下會返回 None。

返回空值是 Python 的核心功能,但是使用顯式return None 語句能更清楚地表達代碼的意圖。

def foo1(value):
    """建議顯示說明返回值 None,方便以後維護"""
    if value:
        return value
    else:
        return None


def foo2(value):
    """純return 語句,相當於`return None`"""
    if value:
        return value
    else:
        return


def foo3(value):
    """無return 語句,也相當於`return None`"""
    if value:
        return value

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章