首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

【Python基础】Python「函数参数」:当函数遇上*args和**kwargs

第1章 引言

1.1 Python语言特性概览

Python以其简洁明了、高度可读性和强大的功能而广受喜爱。在这门语言中,函数参数的设计哲学旨在赋予开发者充分的自由度和灵活性。不同于一些静态类型语言 ,Python支持动态类型,这意味着函数可以接受不同类型的参数,并根据传入的实际参数类型来决定其行为。

1.2 动态类型与函数参数多样性

1.2.1 定义函数的基本语法

想象一下,你正在组织一场盛大的聚会 ,每位客人都可能携带不同的礼物前来。在Python中,定义一个接收这些“礼物”(参数)的“接待函数”,就像这样:

def?greet_guests(name,?gift=None):

print(f"Welcome,?{name}!?You?brought?{gift}.")

此处的name是必需的“位置”参数 ,而gift则是可选的“默认”参数,若客人未携带礼物,则使用预设的None。

1.2.2 固定参数与默认参数的使用

当我们为greet_guests函数添加更多细节时,比如记录每位客人的到访时间和特殊需求,情况就变得更有趣了。这时 ,固定的参数列表可能会限制我们的灵活性。但是别担心,Python提供了*args和**kwargs这两个神奇的工具 ,它们像魔法口袋一样可以容纳任意数量的位置参数和关键字参数。

例如,我们修改后的greet_guests函数允许客人带任意数量的朋友及他们各自的信息:

def?enhanced_greet_guests(name,?arrival_time,?*guests_info,?**special_requests):

#?根据guests_info和special_requests处理更复杂的情况...

pass

在这个函数中 ,*guests_info可以是一组不定长度的名字列表,而**special_requests则可以是一个包含各种个性化需求的字典。这种设计使得我们的函数能够轻松应对各种不可预知的情况 ,展现出Python函数参数的高度灵活性与可扩展性。随着我们接下来深入探讨*args和**kwargs的奥秘,你将更能领略这一特性的魅力所在。

第2章?*args:不确定位置参数

2.1 *args的概念与语法

2.1.1*args的命名约定与意义

在Python函数定义中,*args这个神秘符号犹如一把无形的“收纳袋”,它能帮你收集那些数量不定、无需指定名称的“位置参数”。这里的“*”并非表示乘法 ,而是扮演着“打包”角色,将传入的一串值转化为元组(tuple),供函数内部使用。如此一来 ,无论调用者传递多少个额外的参数,只要以正确顺序排列,都能被*args妥善收纳。

2.1.2 在函数定义中使用*args

设想你是一位热衷烘焙的厨师 ,准备创建一个制作混合果酱的函数。由于顾客口味各异 ,每次需要混合的水果种类和数量均不固定。这时,*args便大显身手:

def?make_fruit_jam(*args):

mixed_fruits?=?[]

for?fruit?in?args:

mixed_fruits.append(fruit)

return?f"Your?custom?jam?contains:?{',?'.join(mixed_fruits)}"

此函数接受任意数量的水果名作为参数,将它们加入到mixed_fruits列表中,并最终返回一份个性化的果酱配方。

2.2?*args实战应用

2.2.1 处理不确定数量的数值输入

在进行数学运算时,你可能需要编写一个函数来计算一组数值的平均值。有了*args,只需一行代码即可轻松应对:

def?calculate_average(*numbers):

return?sum(numbers)?/?len(numbers)

result?=?calculate_average(4,?9,?16,?25)??#?输出:1?2.2.2 构建通用数据处理函数

假设你负责维护一个数据分析库,需要提供一个函数来处理不同类型的数据集。利用*args,你可以创建一个泛化的数据加载函数:

def?load_data(*data_sources):

loaded_data?=?[]

for?source?in?data_sources:

loaded_data.extend(load_single_source(source))

return?loaded_data

#?调用示例

combined_data?=?load_data('sales.csv',?'customer_feedback.json')2.2.3 作为其他函数的封装工具

在编写高级抽象时,*args有助于简化函数间的交互。例如,封装一个通用的绘图函数,将具体绘图任务委托给其他库:

def?plot_graph(title,?*plot_args,?library='matplotlib'):

if?library?==?'matplotlib':

import?matplotlib.pyplot?as?plt

plt.plot(*plot_args)

plt.title(title)

plt.show()

elif?library?==?'seaborn':

import?seaborn?as?sns

sns.lineplot(data=plot_args)

plt.title(title)

plt.show()2.3?*args与函数调用2.3.1 使用列表、元组传递位置参数

当已有一组数据存储在列表或元组中 ,直接将其作为*args传入函数,无需手动展开:

fruit_list?=?['apple',?'banana',?'cherry']

custom_jam?=?make_fruit_jam(*fruit_list)??#?直接传递列表

fruit_tuple?=?('pear',?'orange',?'kiwi')

another_jam?=?make_fruit_jam(*fruit_tuple)??#?直接传递元组2.3.2 结合固定参数与默认参数调用函数

在实际使用中,*args经常与固定参数和默认参数共存。例如,调整上述calculate_average函数,使其包含一个可选的权重参数:

def?weighted_average(value1,?weight1=1,?value2=0,?weight2=0,?*additional_values_weights):

total_value?=?value1?*?weight1?+?value2?*?weight2

for?value,?weight?in?zip(additional_values_weights[::2],?additional_values_weights[1::2]):

total_value?+=?value?*?weight

total_weight?=?weight1?+?weight2?+?sum(additional_values_weights[1::2])

return?total_value?/?total_weight

result?=?weighted_average(10,?0.25,?20,?0.75,?30,?0.2,?40,?0.5)??#?输出:26.667

通过以上示例,我们见识到了*args的强大之处,它犹如一块多功能拼图 ,让Python函数在面对复杂多变的需求时仍能保持优雅与适应力。接下来,我们将探索另一个神器——**kwargs,看它是如何帮助我们驾驭关键字参数的海洋。

第3章?**kwargs:关键字参数

3.1?**kwargs的概念与语法

3.1.1**kwargs的命名约定与意义

在Python函数参数家族中,**kwargs如同一位亲切体贴的管家,它能收集并整理所有无法预先确定名字的关键字参数,将它们打包成一个字典(dictionary)。这里的两个星号**就像是魔法师的咒语,将传入的键值对集合转化为字典结构 ,使得函数能够灵活地处理未知数量和名称的配置选项。

3.1.2 在函数定义中使用**kwargs

假设有这么一个场景,你正要举办一场晚会,每位来宾可能有自己特定的需求,如饮食偏好、座位安排等。此时,我们可以创建一个prepare_party函数,借助**kwargs来满足各种个性化需求:

def?prepare_party(guest_name,?**preferences):

print(f"Preparing?party?for?{guest_name}...")

for?preference,?detail?in?preferences.items():

print(f"-?Taking?care?of?{preference}:?{detail}")3.2?**kwargs实战应用3.2.1 创建高度定制化的配置接口

在开发软件时 ,用户常常需要自定义设置以匹配自己的工作流程。通过**kwargs,你可以轻松创建一个能够接受任意数量和名称配置项的函数:

def?configure_app(**settings):

for?key,?value?in?settings.items():

if?key?in?AVAILABLE_SETTINGS:

set_setting(key,?value)

else:

print(f"Unknown?setting?'{key}'?ignored.")

configure_app(theme='dark',?font_size=14,?enable_notifications=False)3.2.2 实现函数的多继承与接口兼容

在面向对象编程中,有时我们需要对接多种接口或者合并多个类的功能。**kwargs可以帮助我们在子类中捕获父类方法未使用的参数 ,从而实现统一接口:

class?BaseClass:

def?__init__(self,?common_arg):

self.common?=?common_arg

class?ChildClass(BaseClass):

def?__init__(self,?common_arg,?**specific_options):

super().__init__(common_arg)

for?option,?value?in?specific_options.items():

setattr(self,?option,?value)

child?=?ChildClass("shared?data",?extra_option1="value1",?extra_option2="value2")3.2.3 作为装饰器参数传递机制

装饰器通常用于增强函数功能,但原始函数可能接受不同参数。借助**kwargs,装饰器能原样传递未知参数给被装饰的函数:

def?logging_decorator(func):

def?wrapper(**kwargs):

print("Logging?start...")

result?=?func(**kwargs)

print("Logging?end.")

return?result

return?wrapper

@logging_decorator

def?process_data(a,?b,?c,?d=1):

#?执行数据处理逻辑...

process_data(1,?2,?3,?d=4)3.3?**kwargs与函数调用3.3.1 使用字典传递关键字参数

当你有一个包含所需参数名称和对应值的字典时 ,可以通过**操作符将其展开为关键字参数:

options?=?{'theme':?'light',?'font_size':?12}

configure_app(**options)3.3.2 动态构建参数字典

在运行时,可以根据条件动态创建参数字典 ,并将其传递给带有**kwargs的函数:

dynamic_preferences?=?{}

if?user_wants_dark_mode():

dynamic_preferences['theme']?=?'dark'

else:

dynamic_preferences['theme']?=?'light'

configure_app(**dynamic_preferences)3.3.3 结合*args与固定参数调用函数

*args和**kwargs可以在同一个函数调用中和谐共存 ,共同为函数注入无限可能:

def?versatile_function(required_arg,?*positional_args,?**keyword_args):

...

args_list?=?[1,?2,?3]

kwargs_dict?=?{'option1':?'value1',?'option2':?'value2'}

versatile_function("must?have",?*args_list,?**kwargs_dict)

通过深入了解**kwargs的奇妙运用,我们得以窥见Python函数参数设计的精妙之处,也更加体会到其带来的强大灵活性。接下来,在第四章中 ,我们将探讨如何巧妙地将*args与**kwargs结合起来,进一步拓展函数接口的可能性。

第4章?*args与**kwargs的组合使用

4.1 参数顺序与收集规则

4.1.1*args与固定参数的关系

在函数定义中,*args紧跟在固定参数之后,扮演着“剩余位置参数”的角色。它如同一个无形的收纳箱,收集所有未被固定参数“认领”的位置参数。一旦遇到*args,后续的所有参数(除非另有**kwargs)都将被视为位置参数并被放入这个收纳箱。

def?function(a,?b,?*args):

#?a,?b是固定参数

#?args收集剩余位置参数

...

function(1,?2,?3,?4,?5)??#?a=1,?b=2,?args=(3,?4,?5)4.1.2**kwargs与其他参数的结合

**kwargs则位于参数列表的末尾 ,如同一个精巧的文件柜,负责收纳所有以键值对形式提供的额外信息。它与固定参数、默认参数以及*args和谐共处 ,确保无论有多少未知的、带有名称的参数,都能被妥善保管。

def?function(a,?b=1,?*args,?**kwargs):

#?a是固定参数

#?b是默认参数

#?args收集剩余位置参数

#?kwargs收集关键字参数

...

function(1,?2,?3,?4,?name="Alice",?age=30)??#?a=1,?b=2,?args=(3,?4),?kwargs={"name":?"Alice",?"age":?30}4.2 组合使用案例分析4.2.1 复杂数据结构处理函数设计

在处理复杂数据结构(如嵌套列表、字典等)时,*args与**kwargs的组合尤为有用。例如,编写一个函数来统计各类数据的数量:

def?count_items(*args,?**kwargs):

counts?=?{}

for?item?in?args:

counts[item]?=?counts.get(item,?0)?+?1

for?key,?value?in?kwargs.items():

counts[key]?=?counts.get(key,?0)?+?value

return?counts

data?=?[1,?2,?2,?3,?3,?3,?4]

categories?=?{"A":?2,?"B":?1,?"C":?.png}

counts?=?count_items(*data,?**categories)

print(counts)??#?输出:{1:?1,?2:?2,?3:?3,?4:?1,?"A":?2,?"B":?1,?"C":?3}4.2.2 面向对象编程中的灵活构造函数

在OOP中 ,*args与**kwargs可以用来创建高度灵活的构造函数,允许用户在初始化对象时传递任意数量和名称的属性:

class?CustomObject:

def?__init__(self,?required_arg,?*args,?**kwargs):

self.required_arg?=?required_arg

self.additional_args?=?args

self.custom_properties?=?kwargs

obj?=?CustomObject("mandatory",?"optional1",?"optional2",?prop1="value1",?prop2="value2")4.2.3 通用回调函数实现

在需要注册回调函数的场景中 ,如事件处理、异步编程等,使用*args与**kwargs可以让回调函数适应各种调用者的参数需求:

def?generic_callback(*args,?**kwargs):

print(f"Received?callback?with?args:?{args}?and?kwargs:?{kwargs}")

button.on_click(generic_callback)??#?无论按钮点击事件如何传递参数,该回调均可处理

通过巧妙地结合使用*args与**kwargs,Python函数的接口设计变得更加灵活多变 ,能够适应各种复杂场景下的参数传递需求。接下来 ,我们将探讨如何运用这些参数技巧进行高级编程实践。

第5章 高级技巧与最佳实践

5.1 类似*与**操作符的其他应用

5.1.1 在函数返回值中使用*展开序列

在Python中,不仅可以在函数定义中使用*args收集不定数量的位置参数,在函数返回值时也能利用*操作符将序列拆解为单独的元素。这就好比魔术师从帽子里逐一抽出彩带 ,而不是一次性拿出整个包裹。

def?split_sequence(sequence):

return?1,?2,?*sequence

result?=?split_sequence([3,?4,?5])

print(result)??#?输出:(1,?2,?3,?4,?5)5.1.2 在函数调用中使用**展开字典

同样,**操作符也可用于在函数调用时展开字典为关键字参数。想象一下 ,你手中有一本满载指令的魔法书,通过**操作符将每一页的指令一次性释放给魔法阵:

def?wizard_spell(spell_name,?**spell_details):

print(f"Casting?spell?'{spell_name}'?with?details:?{spell_details}")

spell_book?=?{"duration":?"long",?"power":?"high"}

wizard_spell("Fireball",?**spell_book)

#?输出:"Casting?spell?'Fireball'?with?details:?{'duration':?'long',?'power':?'high'}"5.2 参数验证与错误处理5.2.1 检查*args与**kwargs的有效性

在使用*args和**kwargs时,确保传递的参数符合预期至关重要。可以通过条件检查和类型判断来确保参数有效:

def?process_data(*args,?**kwargs):

if?not?all(isinstance(arg,?int)?for?arg?in?args):

raise?TypeError("All?position?arguments?must?be?integers")

for?key,?value?in?kwargs.items():

if?not?isinstance(value,?(int,?float)):

raise?TypeError(f"Argument?'{key}'?must?be?an?integer?or?float")

#?实际数据处理过程...5.2.2 提供清晰的错误提示与异常处理

为了提升用户体验,当参数无效时,应该抛出有意义的异常,并附带详细错误信息:

def?flexible_function(*args,?**kwargs):

try:

validate_args(args)

validate_kwargs(kwargs)

except?ValueError?as?ve:

print(f"Error:?{ve}")

return?None

#?函数主体部分...

def?validate_args(args):

if?len(args)?<?2:

raise?ValueError("At?least?two?positional?arguments?are?required")

def?validate_kwargs(kwargs):

if?"timeout"?in?kwargs?and?not?isinstance(kwargs["timeout"],?int):

raise?ValueError("'timeout'?must?be?an?integer")

#?示例调用

flexible_function("invalid",?timeout="too?long")??#?输出:"Error:?'timeout'?must?be?an?integer"5.3 遵循PEP8编码规范与文档化5.3.1 明确标注函数参数类型与说明

在Python中,尽管动态类型带来了极大的灵活性,但为函数参数添加类型注释有助于代码理解和维护。借助类型提示,可清晰表达*args和**kwargs期望接收的数据类型:

def?robust_function(arg1:?int,?arg2:?str,?*args:?float,?**kwargs:?bool):

"""

...

:param?arg1:?The?first?integer?argument.

:param?arg2:?The?second?string?argument.

:param?args:?Additional?floating-point?arguments.

:param?kwargs:?Keyword?arguments?that?should?be?boolean?values.

"""

pass5.3.2 使用docstring提高代码可读性

编写详细的docstring不仅能帮助其他开发者理解函数的目的、参数和返回值,而且对于IDE自动补全和第三方文档生成工具至关重要。在涉及*args和**kwargs的函数中 ,尤其需要细致描述它们的用途和约束:

def?advanced_processing(input_data,?*other_data,?output_format="json",?**options):

"""

Processes?input?data?and?other?optional?data?with?the?given?options,

returning?results?in?the?specified?output?format.

Args:

input_data?(dict):?The?main?data?to?be?processed.

*other_data?(list):?Additional?data?items?to?incorporate?into?processing.

output_format?(str,?optional):?Desired?output?format.?Defaults?to?"json".

**options?(dict):?Arbitrary?keyword?arguments?to?customize?processing.

Returns:

str?or?dict:?Processed?data?in?the?chosen?output?format.

"""

...

通过掌握这些高级技巧和最佳实践 ,开发者能够更好地利用*args和**kwargs提升代码的健壮性和可读性,同时适应更多的应用场景。接下来 ,我们将通过实际项目的函数构建,进一步演示这些概念在实战中的应用。

第6章 实战演练:构建实际项目中的函数

6.1 数据清洗与预处理任务

6.1.1 设计接受多种参数的数据清理函数

在处理真实世界的脏数据时,我们常常需要一个能应对各种复杂情况的清理函数。借助*args与**kwargs,我们可以创建一个通用的clean_data函数,它能接受多种数据清理选项:

def?clean_data(dataset,?*args,?remove_nulls=True,?convert_dates=True,?**kwargs):

cleaned_dataset?=?dataset.copy()

if?remove_nulls:

cleaned_dataset.dropna(inplace=True)

if?convert_dates:

for?column?in?cleaned_dataset.columns:

if?cleaned_dataset[column].dtype?==?'object':

try:

cleaned_dataset[column]?=?pd.to_datetime(cleaned_dataset[column])

except?ValueError:

pass

#?处理额外清理选项

for?arg?in?args:

if?hasattr(arg,?'__call__'):

cleaned_dataset?=?arg(cleaned_dataset)

for?key,?value?in?kwargs.items():

if?hasattr(cleaned_dataset,?key)?and?hasattr(value,?'__call__'):

cleaned_dataset[key]?=?value(cleaned_dataset[key])

return?cleaned_dataset6.1.2 使用*args与**kwargs实现灵活配置

现在 ,我们可以根据实际需求灵活调用clean_data函数,指定不同的清理选项:

import?pandas?as?pd

dirty_data?=?pd.read_csv('sales_data.csv')

#?基础清理:去除空值并转换日期列

cleaned_data?=?clean_data(dirty_data)

#?添加自定义清理步骤:移除异常值

def?remove_outliers(data):

q1?=?data.quantile(0.25)

q3?=?data.quantile(0.75)

iqr?=?q3?-?q1

lower_bound?=?q1?-?1.5?*?iqr

upper_bound?=?q3?+?1.5?*?iqr

return?data[(data?>?lower_bound)?&?(data?<?upper_bound)]

#?高级清理:去除空值、转换日期列并移除异常值

advanced_cleaned_data?=?clean_data(dirty_data,?remove_outliers,?remove_nulls=True,?convert_dates=True)

#?使用关键字参数进行特定列清理:标准化价格列

from?sklearn.preprocessing?import?StandardScaler

price_scaler?=?StandardScaler()

cleaned_data_with_scaled_price?=?clean_data(dirty_data,?price_scaler=price_scaler.fit_transform)6.2 Web服务API接口设计6.2.1 利用**kwargs处理API查询参数

在构建Web API时,用户可能传入各种自定义查询参数。使用**kwargs,我们可以轻松地收集并处理这些参数:

from?flask?import?Flask,?request,?jsonify

app?=?Flask(__name__)

@app.route('/api/search',?methods=['GET'])

def?search_api():

query_params?=?request.args.to_dict()??#?获取请求中的查询参数

results?=?perform_search(**query_params)??#?使用**kwargs传递给处理函数

return?jsonify(results)

def?perform_search(keyword=None,?category=None,?limit=10,?offset=0):

#?根据参数执行搜索逻辑

...6.2.2 实现参数自动转换与校验逻辑

为了确保API接口的安全性和一致性 ,我们可以利用**kwargs在函数内部实现参数的自动转换与校验:

from?typing?import?Dict,?Any

from?flask?import?Flask,?request,?jsonify,?abort

from?werkzeug.exceptions?import?BadRequest

app?=?Flask(__name__)

def?validate_and_convert_params(params:?Dict[str,?Any])?->?Dict[str,?Any]:

validated_params?=?{}

if?'limit'?in?params:

try:

limit?=?int(params['limit'])

if?limit?<=?0:

raise?ValueError("Limit?must?be?a?positive?integer.")

validated_params['limit']?=?limit

except?ValueError?as?e:

raise?BadRequest(str(e))

#?同理处理其他参数...

return?validated_params

@app.route('/api/search',?methods=['GET'])

def?search_api():

query_params?=?request.args.to_dict()

validated_params?=?validate_and_convert_params(query_params)

results?=?perform_search(**validated_params)

return?jsonify(results)6.3 GUI应用程序事件处理6.3.1 使用*args处理多参数事件回调

在GUI编程中,事件处理器可能需要处理来自不同控件的多种数据。*args可以轻松收集这些数据:

from?tkinter?import?*

root?=?Tk()

def?on_button_click(*args):

print(f"Button?clicked?with?arguments:?{args}")

button?=?Button(root,?text="Click?me!",?command=lambda:?on_button_click("Hello",?"World"))

button.pack()

root.mainloop()6.3.2 通过**kwargs传递额外上下文信息

有时,事件处理器可能需要访问触发事件的控件本身或其他相关状态。**kwargs可以帮助我们传递这些附加信息:

from?tkinter?import?*

class?CustomWidget(Frame):

def?__init__(self,?master,?**kwargs):

super().__init__(master,?**kwargs)

self.label?=?Label(self,?text="")

self.button?=?Button(self,?text="Update?label",?command=self.update_label)

self.label.pack()

self.button.pack()

def?update_label(self):

new_text?=?self.button.cget("text")?+?"?clicked!"

self.label.config(text=new_text)

root?=?Tk()

widget?=?CustomWidget(root,?bg="lightblue")

widget.pack()

root.mainloop()

通过这些实战案例,我们见证了*args与**kwargs在实际项目中的强大威力。它们使我们的函数更具适应性和扩展性 ,能够从容应对复杂多变的需求。

第7章 总结

Python的*args与**kwargs犹如编程工具箱中的瑞士军刀,赋予函数无尽的灵活性与可扩展性。它们分别代表了任意数量的位置参数和关键字参数,极大地提升了函数接口设计的包容性。在实践中 ,*args常用于处理不确定数量的数值输入或构建通用数据处理函数,而**kwargs则助力创建高度定制化配置接口、实现函数多继承与装饰器参数传递。两者结合使用时,遵循特定顺序规则 ,可在复杂数据结构处理、面向对象编程及事件回调等场景发挥关键作用。

此外,巧妙利用*与**操作符还能在函数返回值与调用中展现独特优势。参数验证与错误处理则是保证代码质量不可或缺的部分,而遵循PEP8编码规范和强化docstring文档 ,使代码更具可读性与维护性。在实战中,无论是数据清洗任务、Web服务API设计还是GUI应用程序事件处理,*args与**kwargs都是实现灵活配置与高效通信的重要手段。

总之,二者的核心价值在于促进程序设计的模块化、适应性强 ,随着Python新版本对可变参数支持的加强及其与类型提示的有机结合,它们在未来将持续为程序员提供更为强大和直观的编程体验。

  • 发表于:
  • 原文链接https://page.om.qq.com/page/O5FjBN1HiLOBy5zwdVH8YsUQ0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券
http://www.vxiaotou.com