【Python】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方法获取实际的参数信息。

    作者:EulerBlind

    物联沃分享整理
    物联沃-IOTWORD物联网 » 【Python】Python参数详解

    发表回复