自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Python `*args` 和 `**kwargs`:優(yōu)雅處理可變參數(shù)的終極指南 & 配合 frozenset 實(shí)現(xiàn)通用緩存器

開發(fā) 前端
在Python開發(fā)中,我們經(jīng)常會(huì)遇到需要處理不定數(shù)量參數(shù)的場景。今天就來聊聊Python中的*args和**kwargs,看看它們?nèi)绾螏臀覀儍?yōu)雅地解決這類問題。

在Python開發(fā)中,我們經(jīng)常會(huì)遇到需要處理不定數(shù)量參數(shù)的場景。今天就來聊聊Python中的*args和**kwargs,看看它們?nèi)绾螏臀覀儍?yōu)雅地解決這類問題。

從一個(gè)實(shí)際場景說起

假設(shè)你正在開發(fā)一個(gè)數(shù)據(jù)處理框架,需要實(shí)現(xiàn)一個(gè)通用的函數(shù)裝飾器來記錄函數(shù)執(zhí)行時(shí)間:

import time
from functools import wraps

def timer(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        end = time.perf_counter()
        print(f"{func.__name__} 執(zhí)行耗時(shí): {end - start:.6f} 秒")
        return result
    return wrapper

@timer
def process_data(data, threshold=0.5):
    # 模擬數(shù)據(jù)處理
    time.sleep(1)
    return [x for x in data if x > threshold]

# 使用示例
result = process_data([1, 2, 3, 0.1, 0.4])
# 輸出:process_data 執(zhí)行耗時(shí): 1.003865 秒

注意到裝飾器中的*args和**kwargs了嗎?它們讓我們的裝飾器可以適配任意參數(shù)的函數(shù)。

*args :處理位置參數(shù)

*args允許函數(shù)接收任意數(shù)量的位置參數(shù),這些參數(shù)會(huì)被打包成一個(gè)元組。

def sum_all(*numbers):
    return sum(numbers)

# 以下調(diào)用都是有效的
print(sum_all(1, 2))          # 3
print(sum_all(1, 2, 3, 4))    # 10

**kwargs :處理關(guān)鍵字參數(shù)

**kwargs則用于接收任意數(shù)量的關(guān)鍵字參數(shù),這些參數(shù)會(huì)被打包成一個(gè)字典。

def print_user_info(**info):
    for key, value in info.items():
        print(f"{key}: {value}")

# 可以傳入任意數(shù)量的命名參數(shù)
print_user_info(name="Alice", age=30, city="Shanghai")

解包操作: * 和 ** 的另一面

除了在函數(shù)定義時(shí)使用,*和**還可以用于解包序列和字典:

def greet(name, age, city):
    print(f"你好,{name}!你{age}歲了,來自{city}?")

# 使用*解包列表/元組
user_data = ["Bob", 25, "Beijing"]
greet(*user_data)  # 你好,Bob!你25歲了,來自Beijing?

# 使用**解包字典
user_dict = {"name": "Charlie", "age": 35, "city": "Guangzhou"}
greet(**user_dict)   # 你好,Charlie!你35歲了,來自Guangzhou?

高級(jí)應(yīng)用:混合使用與順序規(guī)則

在實(shí)際開發(fā)中,我們經(jīng)常需要混合使用這些特性:

def complex_function(x, y, *args, default=None, **kwargs):
    print(f"x: {x}")
    print(f"y: {y}")
    print(f"args: {args}")
    print(f"default: {default}")
    print(f"kwargs: {kwargs}")

# 調(diào)用示例
complex_function(1, 2, 3, 4, default="test", extra=True, debug=False)

這里有個(gè)重要的順序規(guī)則:

  1. 普通位置參數(shù)
  2. *args
  3. 默認(rèn)參數(shù)
  4. **kwargs

實(shí)用技巧:使用 *args 和 **kwargs 實(shí)現(xiàn)通用緩存裝飾器

在開發(fā)中,經(jīng)常需要在不修改原函數(shù)簽名的情況下添加新功能:

import time
from typing import Any, Callable
from functools import wraps

class Cache:
    def __init__(self):
        self._cache = {}
    
    def cached_call(self, func: Callable[..., Any], *args, **kwargs) -> Any:
        # 使用frozenset處理kwargs,確保{a:1, b:2}和{b:2, a:1}被視為相同的調(diào)用
        key = (func.__name__, args, frozenset(kwargs.items()))

        if key not in self._cache:
            print(f"Cache miss for {func.__name__}, calculating...")
            start = time.perf_counter()
            self._cache[key] = func(*args, **kwargs)
            end = time.perf_counter()
        else:
            print(f"Cache hit for {func.__name__}, returning cached result")
        
        return self._cache[key]

# 創(chuàng)建緩存實(shí)例
cache = Cache()

def expensive_operation(x: int, y: int, z: int = 1) -> int:
    """模擬耗時(shí)操作"""
    time.sleep(2)  # 模擬耗時(shí)計(jì)算
    return x + y + z

def measure_time(func: Callable, *args, **kwargs) -> None:
    """測(cè)量函數(shù)執(zhí)行時(shí)間"""
    start = time.perf_counter()
    result = func(*args, **kwargs)
    end = time.perf_counter()
    print(f"Result: {result}")
    print(f"Time taken: {end - start:.2f} seconds\n")
    return result

# 演示不同場景下的緩存效果
print("第一次調(diào)用(無緩存):")
measure_time(cache.cached_call, expensive_operation, 1, 2, z=3)

print("第二次調(diào)用(使用緩存):")
measure_time(cache.cached_call, expensive_operation, 1, 2, z=3)

print("不同參數(shù)順序的調(diào)用(展示frozenset的作用):")
# 注意這里kwargs的順序不同,但應(yīng)該命中相同的緩存
result3 = cache.cached_call(expensive_operation, x=1, y=2, z=3)
result4 = cache.cached_call(expensive_operation, y=2, x=1, z=3)

輸出:

第一次調(diào)用(無緩存):
Cache miss for expensive_operation, calculating...
Result: 6
Time taken: 2.01 seconds

第二次調(diào)用(使用緩存):
Cache hit for expensive_operation, returning cached result
Result: 6
Time taken: 0.00 seconds

不同參數(shù)順序的調(diào)用(展示frozenset的作用):
Cache miss for expensive_operation, calculating...
Cache hit for expensive_operation, returning cached result

注意,在實(shí)現(xiàn)緩存時(shí),我們需要一個(gè)可哈希(hashable)的鍵來唯一標(biāo)識(shí)函數(shù)調(diào)用。但是普通的set和dict是可變的,因此不能作為字典的鍵。Python 的 frozenset 就是為了解決這個(gè)問題 - 它是不可變的集合類型。

關(guān)于frozenset的幾個(gè)重要特點(diǎn)

  1. 不可變性:一旦創(chuàng)建就不能修改,這使它可以作為字典的鍵
# 這是允許的
d = {frozenset([1, 2, 3]): "value"}

# 這會(huì)報(bào)錯(cuò)
s = set([1, 2, 3])
d = {s: "value"}  # TypeError: unhashable type: 'set'
  1. 順序無關(guān)性:
# 這兩個(gè)frozenset是相等的
fs1 = frozenset([1, 2, 3])
fs2 = frozenset([3, 1, 2])
print(fs1 == fs2)  # True
  1. 性能考慮:
# 下面這種寫法更高效
key = (func.__name__, args, frozenset(kwargs.items()))

# 而不是
key = (func.__name__, args, tuple(sorted(kwargs.items())))

關(guān)于frozenset的注意事項(xiàng)

  1. frozenset只能包含可哈希的元素。例如,你不能創(chuàng)建包含列表或字典的frozenset。
  2. 在我們的緩存實(shí)現(xiàn)中,如果函數(shù)參數(shù)包含不可哈希的類型(如列表),需要額外處理:
def make_hashable(obj):
    """將對(duì)象轉(zhuǎn)換為可哈希的形式"""
    if isinstance(obj, (tuple, list)):
        return tuple(make_hashable(o) for o in obj)
    elif isinstance(obj, dict):
        return frozenset((k, make_hashable(v)) for k, v in obj.items())
    elif isinstance(obj, set):
        return frozenset(make_hashable(o) for o in obj)
    return obj

# 改進(jìn)的緩存鍵生成
key = (func.__name__, make_hashable(args), make_hashable(kwargs))

一些 *args 和 **kwargs 的注意事項(xiàng)

  1. 參數(shù)名稱不一定非要用args和kwargs,但這是約定俗成的命名。
  2. 在函數(shù)定義中,*args必須在**kwargs之前。
  3. 在Python3 中,可以在*args之后定義強(qiáng)制關(guān)鍵字參數(shù)。

總結(jié)

*args和**kwargs是Python中非常強(qiáng)大的特性,它們讓我們能夠:

  • 編寫更靈活的函數(shù)和裝飾器
  • 實(shí)現(xiàn)參數(shù)轉(zhuǎn)發(fā)
  • 處理不定量的參數(shù)

掌握這些特性,可以讓我們的代碼更加優(yōu)雅和通用。在日常開發(fā)中,合理使用這些特性可以大大提高代碼的可維護(hù)性和可擴(kuò)展性。

責(zé)任編輯:武曉燕 來源: Piper蛋窩
相關(guān)推薦

2023-03-09 16:39:23

Python傳遞參數(shù)

2020-07-19 08:15:41

PythonDebug

2025-02-05 08:43:40

2022-05-03 10:43:43

SpringJava

2020-07-11 09:25:15

Python編程語言代碼

2017-05-19 12:00:25

Python數(shù)據(jù)類型集合

2023-05-05 17:20:04

2018-08-17 04:53:36

2021-01-06 05:29:04

C語言參數(shù)應(yīng)用

2023-11-30 15:53:43

2021-06-21 09:30:12

@wraps 修飾器Python

2024-10-06 14:01:47

Python裝飾器對(duì)象編程

2025-04-01 00:06:50

JavaK8sSpring

2025-03-11 00:54:42

2012-08-21 06:53:00

測(cè)試軟件測(cè)試

2024-03-11 05:00:00

Python集合開發(fā)

2015-07-20 09:39:41

Java日志終極指南

2017-03-27 21:14:32

Linux日志指南

2023-06-12 17:54:50

2021-09-10 14:05:14

預(yù)測(cè)分析大數(shù)據(jù)分析大數(shù)據(jù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)