写 Python 函数别再死抠参数了,这招让代码瞬间灵活

你有没有遇到过这种场景:写了一个函数,参数死板得像石头,非得按照顺序一个个传。如果临时想多加几个参数,立马报错。于是你只能不停改函数定义,改着改着自己都乱了。

其实 Python 早就给了我们一个神器,专门解决这种"参数不确定"的尴尬场景------那就是 *args 和 **kwargs。别被这两个名字吓到,它们的本质其实就是两个袋子:

  • 一个袋子专门装位置参数(*args → 元组);
  • 另一个袋子专门装关键字参数(**kwargs → 字典)。

只要学会用这两个袋子,你的函数立刻灵活到飞起。


1. 最直观的感受:把参数全都装起来

我们先看个最常见的例子:

python 复制代码
def demo_args(a, b, *args, **kwargs):
    print(f"标准参数: {a}, {b}")
    print(f"位置参数 (*args): {args} (类型: {type(args)})")
    print(f"关键字参数 (**kwargs): {kwargs} (类型: {type(kwargs)})")

demo_args(1, 2, 3, 4, 5, name="Alice", age=30)

输出结果一目了然:

  • 前两个是标准参数;
  • 额外的数字自动打包进 args,形成一个元组;
  • 关键字参数则全都放进 kwargs,成了字典。

是不是很像餐厅点菜?前两个是必点菜,剩下的都扔进大盘子里,不用提前规定数量。


2. 为什么说它是"解放双手"的神器?

来想象一个真实的场景:你写了一个计算器函数,有时候只想加法,有时候要算平均值,有时候还要控制小数点精度。如果不用 *args 和 **kwargs,你得写一堆重复函数。

看看这个版本:

python 复制代码
def safe_calculator(op, *nums, **options):
    # 验证数字类型
    if not all(isinstance(n, (int, float)) for n in nums):
        raise TypeError("必须输入数字")

    # 处理选项
    precision = options.pop('precision', 2)  # 默认精度2
    if options:  # 检查未处理的关键字
        raise ValueError(f"无效选项: {', '.join(options.keys())}")

    # 执行操作
    if op == 'add':
        result = sum(nums)
    elif op == 'avg':
        result = sum(nums) / len(nums)
    else:
        raise ValueError("未知操作")

    return round(result, precision)

print(safe_calculator('avg', 1.5, 2, 3.7, precision=3))  # 2.400

注意几个亮点:

  • 位置参数随便丢几个数字进去就能算;
  • 精度控制这种额外需求,直接扔进 kwargs
  • 未来如果想扩展新功能,直接加关键字参数即可,完全不用改函数结构。

这就是 *args 和 **kwargs 的精髓:让函数保持开放扩展,而不是死板固定。


3. 代码复用的幕后英雄:装饰器和继承

很多人刚学 Python,觉得装饰器、继承这些高阶语法晦涩难懂。但只要你知道 *args 和 **kwargs,其实它们背后就是在做参数打包和转发。

(1)装饰器的例子

python 复制代码
def debug_decorator(func):
    def wrapper(*args, **kwargs):
        print(f"调用函数: {func.__name__}({args}, {kwargs})")
        result = func(*args, **kwargs)
        print(f"返回结果: {result}")
        return result
    return wrapper

@debug_decorator
def multiply(a, b):
    return a * b

multiply(5, b=6)

这里的 wrapper 就像一个"万能接口",不管传什么参数,它都能兜住,再原封不动转给真正的函数。这就是为什么装饰器能无缝给任何函数加功能。

(2)继承的例子

python 复制代码
class Animal:
    def __init__(self, name, legs):
        self.name = name
        self.legs = legs

class Bird(Animal):
    def __init__(self, can_fly, *args, **kwargs):
        super().__init__(*args, **kwargs)  # 透传参数
        self.can_fly = can_fly

parrot = Bird(can_fly=True, name="Polly", legs=2)
print(f"{parrot.name} 有 {parrot.legs} 条腿, 会飞: {parrot.can_fly}")

如果不用 *args 和 **kwargs,你就得在子类里重复写一堆父类的参数传递逻辑。现在只要透传一下,父类需要啥,它自动帮你装好送过去。这就是优雅。


4. 容易踩的坑:这几个错误一定要避开

别看 *args 和 **kwargs 灵活,但也有一些"暗礁",很多人第一次都会踩。

  • 参数顺序不能乱正确顺序是:标准参数 → *args → 关键字参数 → **kwargs。 否则报错让你怀疑人生。

  • 别重复赋值

    python 复制代码
    def problematic(x, **kwargs):
        print(x, kwargs)
    
    problematic(1, x=2)  # 报错:x重复赋值
  • 调用时顺序要对 位置参数要放前面,关键字参数在后面,不然会直接 SyntaxError


5. 实用小技巧:写代码更优雅

强制关键字参数

python 复制代码
def safe_divide(numerator, denominator, *, ignore_zero=False):
    if denominator == 0:
        return float('inf') if ignore_zero else None
    return numerator / denominator

print(safe_divide(5, 0))  # None
print(safe_divide(5, 0, ignore_zero=True))  # inf

加一个 *,后面参数必须写成关键字形式,避免调用时传错位置。

字典合并

arduino 复制代码
def merge_config(defaults, **overrides):
    return {**defaults, **overrides}

config = merge_config({"color": "red", "size": 10}, size=20, opacity=0.5)
# {'color': 'red', 'size': 20, 'opacity': 0.5}

API 参数转发

python 复制代码
class Database:
    def execute(self, query, *params, **options):
        print(f"执行查询: {query}")
        print(f"参数: {params}")
        print(f"选项: {options}")

class UserService:
    def __init__(self, db):
        self.db = db
    
    def get_user(self, user_id, use_cache=True):
        return self.db.execute(
            "SELECT * FROM users WHERE id = %s",
            user_id,
            timeout=10,
            cache=use_cache
        )

db = Database()
service = UserService(db)
service.get_user(123, use_cache=False)

看到了吗?转发参数就像"物流分拣中心",原封不动把数据派送给下一层。


6. 总结:*args 和 **kwargs 就是 Python 的"保险丝"

很多人写 Python 程序时,最怕的就是"未来需求变了"。今天只要两个参数,明天可能要十个。你不能每次都把函数拆了重写,这样太费劲。

而 *args 和 **kwargs,正是 Python 设计给你的灵活接口:

  • *args → 位置参数打包成元组;
  • **kwargs → 关键字参数打包成字典;
  • 它们能让函数写得既简洁,又能轻松扩展;
  • 常见应用:装饰器、继承、参数校验、API设计......

记住一句话:**当你不确定未来会传什么参数时,就放心交给 *args 和 kwargs。


相关推荐
杜子不疼.9 分钟前
《Python学习之第三方库:开启无限可能》
开发语言·python·学习
古夕10 分钟前
my-first-ai-web_问题记录03——NextJS 项目框架基础扫盲
前端·javascript·react.js
曲意已决42 分钟前
《深入源码理解webpac构建流程》
前端·javascript
去伪存真1 小时前
前端如何让一套构建产物,可以部署多个环境?
前端
KubeSphere1 小时前
EdgeWize v3.1.1 边缘 AI 网关功能深度解析:打造企业级边缘智能新体验
前端
青衫客361 小时前
用 Python 实现一个“小型 ReAct 智能体”:思维链 + 工具调用 + 环境交互
python·大模型·llm·react
掘金安东尼1 小时前
解读 hidden=until-found 属性
前端·javascript·面试
1024小神1 小时前
jsPDF 不同屏幕尺寸 生成的pdf不一致,怎么解决
前端·javascript
滕本尊1 小时前
构建可扩展的 DSL 驱动前端框架:从 CRUD 到领域模型的跃迁
前端·全栈
借月1 小时前
高德地图绘制工具全解析:线路、矩形、圆形、多边形绘制与编辑指南 🗺️✏️
前端·vue.js