【Python】解锁函数的神奇之门:参数的妙用与灵活运用

目录

Python参数种类

Python参数定义

不定参数接收

限定传入参数方式

具体示例

位置参数示例

关键词参数示例

两种方法都支持的形式

参数默认值(可选参数)

参数默认值定义

引用类型默认值异常行为

参数类型声明

参数类型定义申明

函数返回值类型声明

参数与装饰器


Python参数种类

总体来看,Python支持两种类型的方法参数,其类型分别为:

  • 位置参数(Positional Parameters
  • 关键词参数(Keyword Parameters

然后我们看下参数的传递形式,假如有如下函数定义:

def f(a,b,c):
	...

如果我们通过如下方式调用,则称为位置参数:

f(1,2,3)
# 这里的 1,2,3 分别对应位置 a,b,c ,故这里是位置参数 

args=[1,2,3]
f(*args)

如果我们通过如下方式调用,则称为关键词参数。这种方式下,我们不用关心参数位置,而是使用显示指定的方式指定每个参数的值,相比之下,这种方式更明确每个参数的值。

f(b=2,c=3,a=1)
# 或者
kwargs = {"b":2,"c":3,"a":1}
f(**kwargs)

⚠️当我们在给定参数时候,关键词参数后,不可跟位置参数,即如下调用方式是错误的

>>> f(2,b=3,1)
SyntaxError: positional argument follows keyword argument

Python参数定义

不定参数接收

最常见的形式:

def f(*args,**kwargs):
	...

其中,args用于接收位置参数,即args类型是列表;kwargs用于接收keyword参数,即kwargs是一个字典。

限定传入参数方式

从官方给的这个示例说起

def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
      -----------    ----------     ----------
        |             |                  |
        |       位置参数/关键词参数         |
        |                                - 只能关键词参数
         -- 只能是位置参数

根据上述示例,不难看出,在方法定义的时候,我们可以通过特定的两个符号/*将参数分开。即/前面的只能是未知参数,/* 之间的参数即可使位置参数,也可以是关键词参数,*后面的参数必须是关键词参数。

考虑到不定参数同时存在的情况,支持如下定义形式

def f(a,/,b,c,*args,**kwargs):
	...
# 或
def f(a,/,b,c,*args):
	...
# 或
def f(a,/,b,c,**kwargs):
	...
# 或
ef f(a,/,b,c,*,d,**kwargs):
	...

具体示例

位置参数示例

方法定义:

def f(pos1,pos2,/):
	...

正确调用方式

f(1,2)

错误调用方式及其报错

>>> f(1,pos2=2)
TypeError: f() got some positional-only arguments passed as keyword arguments: 'pos2'
>>> f(pos1=1,pos2=2)
TypeError: f() got some positional-only arguments passed as keyword arguments: 'pos1, pos2'

关键词参数示例

方法定义

def f(*,pos1,pos2):
	...

正确调用方式

>>> f(pos1=1,pos2=2)

错误调用方式及其异常

>>> f(1,2)
TypeError: f() takes 0 positional arguments but 2 were given
>>> f(1,pos2=2)
TypeError: f() takes 0 positional arguments but 1 positional argument (and 1 keyword-only argument) were given

两种方法都支持的形式

方法定义

def f(a,/,pos1,pos2,*,b):
	...

⚠️ 如下方式定义会抛出异常

>>> def f(/,pos1,pos2,*):
			...
SyntaxError: invalid syntax
# / 前面必须有参数, * 后面必须有参数

正确调用方式

>>> f(1,2,3,b=4)
>>> f(1,2,pos2=3,b=4)

参数默认值(可选参数)

Python中,给定默认值的参数都是可选参数。

参数默认值定义

Python函数定义的时候,按照参数顺序,从第一个给定默认值之后的参数之后,必须提供默认值

def f(a=1,b=2,c=None):
	...

异常定义及其错误

>>> def f(a,b=2,c):
			...
def f(a,b=2,c):
             ^
SyntaxError: non-default argument follows default argument

引用类型默认值异常行为

有的时候,你可能需传入一个默认的列表,如

def f(a:list=[]):
  a.append(1)
  return a

当我们如下操作,会发现第二次的输出并不是我们希望的

f()  # 输出 [1]
f()  # 输出 [1, 1]

正确应该采用如下方式定义

def f(a: list = None):
    if a is None:
        a = []
    a.append(1)
    return a

# 字典参数的情况
def f(a: dict = None):
    if a is None:
        a = {}

参数类型声明

Python是动态类型语言,其变量的类型在运行时才确定。虽然这种灵活性为开发者带来了方便,但也可能导致代码在运行时发生类型错误。为了提高代码的可维护性和可读性,Python 3.5及以上版本引入了类型提示(Type Hints)。

参数类型定义申明

在函数定义时,可以使用冒号**:**来指定参数的类型

def greet(name: str):
    return "Hello, " + name

函数返回值类型声明

我们可以使用**->**符号来指定函数的返回值类型。例如

def add(x: int, y: int) -> int:
    return x + y

其他类型参数声明参考typing库。

参数与装饰器

首先,我们定义了一系列实用的装饰器,用于测量函数的执行时间。接着,我们定义了几个简单的函数,其中包括不同类型的参数和使用了不同装饰器的函数。

import inspect
from functools import wraps

def timeit(func):
    import time
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        func(*args, **kwargs)
        print(f"cost time: {time.time() - start}")

    return wrapper

def timeit1(name):
    def _timeit(func):
        import time
        @wraps(func)
        def wrapper(*args, **kwargs):
            start = time.time()
            func(*args, **kwargs)
            print(f"{name} cost time: {time.time() - start}")

        return wrapper

    return _timeit

def timeit2(name):
    def _timeit(func):
        import time
        @wraps(func)
        def wrapper(param1, param2, param3):
            start = time.time()
            func(param1, param2, param3)
            print(f"{name} cost time: {time.time() - start}")

        return wrapper

    return _timeit

def func1(param1, param2, param3):
    print(param1, param2, param3)

@timeit
def func2(param1, param2, param3):
    print(param1, param2, param3)

@timeit1("func3")
def func3(param1, param2, param3):
    print(param1, param2, param3)

@timeit2("func4")
def func4(param1, param2, param3):
    print(param1, param2, param3)

def call_func(func):
    arg_spect = inspect.getfullargspec(func)
    print(f"arg_spect: {arg_spect}")
		...

>>> call_func(func1)
arg_spect: FullArgSpec(args=['param1', 'param2', 'param3'], varargs=None, varkw=None, defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={})
--------------------

>>> call_func(func2)
arg_spect: FullArgSpec(args=[], varargs='args', varkw='kwargs', defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={})
--------------------

>>> call_func(func3)
arg_spect: FullArgSpec(args=[], varargs='args', varkw='kwargs', defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={})
--------------------

>>> call_func(func4)
arg_spect: FullArgSpec(args=['param1', 'param2', 'param3'], varargs=None, varkw=None, defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={})
--------------------

通过上述结果,我们可以看到不同类型的函数具有不同的参数信息。对于普通的函数,我们可以通过 inspect.getfullargspec 来获取其参数信息。而对于使用了装饰器的函数,装饰器的实现方式会影响inspect.getfullargspec方法获取实际的参数信息。

相关推荐

  1. MySQL Binlog:数据库变更时间

    2024-02-02 09:44:06       11 阅读
  2. python函数参数

    2024-02-02 09:44:06       20 阅读
  3. 理解 Python 编程中 *args **kwargs

    2024-02-02 09:44:06       19 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-02-02 09:44:06       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-02-02 09:44:06       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-02-02 09:44:06       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-02-02 09:44:06       18 阅读

热门阅读

  1. Codeforces Round 481 (Div. 3)

    2024-02-02 09:44:06       38 阅读
  2. k8s集群master和node添加

    2024-02-02 09:44:06       27 阅读
  3. SQL中Limit的用法详解

    2024-02-02 09:44:06       30 阅读
  4. Tomcat -- server.xml

    2024-02-02 09:44:06       32 阅读
  5. Django从入门到放弃

    2024-02-02 09:44:06       34 阅读
  6. advPython-3

    2024-02-02 09:44:06       30 阅读
  7. Vue中的插槽Slot如何使用

    2024-02-02 09:44:06       25 阅读