Python中的函数定义中的斜杠/和星号*

示例

  • 看一段代码

     def say_hello(name,age=18):
         print(f'你好!我是{name},今年我{age}啦。')
     say_hello('老吴')
     say_hello(name='小钱')
     ​
     # 输出
     你好!我是老吴,今年我18啦。
     你好!我是小钱,今年我18啦。
  • 这个代码你学过python不可能不懂,name是形式参数,也叫位置参数,我们一样可以用参数名=的方式传递(关键字参数)

  • 再看一段代码:这是list列表的index方法的定义描述

     >>> help(list.index)
     Help on method_descriptor:
     ​
     index(self, value, start=0, stop=9223372036854775807, /)
         Return first index of value.
     ​
         Raises ValueError if the value is not present.
  • 我们跟上面一样做个简单的测试

     >>> list1=[2,3,4]
     >>> list1.index(2)
     0
     >>> list1.index(value=2)
     Traceback (most recent call last):
       File "<stdin>", line 1, in <module>
     TypeError: list.index() takes no keyword arguments
    • 提示说list.index没有关键字参数

  • 细心的同学应该发现了,两者的差异在于第二个函数index的定义最后有个/,这个在初学的时候我们一般不会怎么接触到。但其实非常有用。

  • 除了/,还有一个*号,也会出现在函数定义中(注意不是*args和**kwargs)

  • 比如list的sort方法

     >>> help(list.sort)
     Help on method_descriptor:
     ​
     sort(self, /, *, key=None, reverse=False)
         Sort the list in ascending order and return None.
     >>> list1=[3,2,4]
     >>> list1.sort(reverse=True)   # 关键字参数传递方式
     >>> list1
     [4, 3, 2]
     >>> list1.sort(None,reverse=True)    # 不能用位置参数
     Traceback (most recent call last):
       File "<stdin>", line 1, in <module>
     TypeError: sort() takes no positional arguments

斜杠/之前必须是位置参数

  • 这个特性是在python3.8中发布的,仅限位置形参。

     https://docs.python.org/zh-cn/3.8/whatsnew/3.8.html
  • 示例1: 对之前的say_hello函数加个/看看

     def say_hello(name,/,age=18):
         print(f'你好!我是{name},今年我{age}啦。')
     say_hello('老吴')
     say_hello(name='小钱')
     ​
     ​
     你好!我是老吴,今年我18啦。
     ---------------------------------------------------------------------------
     TypeError                                 Traceback (most recent call last)
     <ipython-input-11-e09734cd0de5> in <module>
           2     print(f'你好!我是{name},今年我{age}啦。')
           3 say_hello('老吴')
     ----> 4 say_hello(name='小钱')
     ​
     TypeError: say_hello() got some positional-only arguments passed as keyword arguments: 'name'
    • 非常清楚的提示了,say_hello函数给一个仅限位置参数name用了关键字传参的方式

  • 示例2:放到最后会怎样?

     def say_hello(name,age=18,/):
         print(f'你好!我是{name},今年我{age}啦。')
     say_hello('老吴',19)
     say_hello('小钱',age=19)
     # 输出如下
     你好!我是老吴,今年我19啦。
     ---------------------------------------------------------------------------
     TypeError                                 Traceback (most recent call last)
     <ipython-input-14-911127531c6c> in <module>
           2     print(f'你好!我是{name},今年我{age}啦。')
           3 say_hello('老吴',19)
     ----> 4 say_hello('小钱',age=19)
     ​
     TypeError: say_hello() got some positional-only arguments passed as keyword arguments: 'age'
    • 对于默认值参数age,一样的不能用关键字参数方式传递,只能用位置参数

  • 示例3:/放在中间,对于/后的是没有限制的,下面2个调用都ok的。

    def say_hello(name,/,age):
    print(f'你好!我是{name},今年我{age}啦。')
    say_hello('老吴',18)
    say_hello('小钱',age=18)
  • 比如len

    >>> help(len)
    Help on built-in function len in module builtins: len(obj, /)
    Return the number of items in a container.
    • 你从来不会这样调用,你都不知道有obj这个参数,但其实你应该明白它是有参数的。

      >>> len(obj='ab')
      Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      TypeError: len() takes no keyword arguments

星号*后面必须是关键字参数

  • 没有找到出处

  • 但有了/的基础,你看到这个要求就比较容易理解了。

  • 示例1:

    def say_hello(name,*,age):
    print(f'你好!我是{name},今年我{age}啦。')
    say_hello('小钱',age=18) # * 号后面是关键字参数
    say_hello(name='啊徐',age=18) # * 前面的你随便用什么方式都可以(如果没有/的话)
    say_hello('老吴',18) # 你不能用位置参数的方式来调用 # 输出
    你好!我是小钱,今年我18啦。
    你好!我是啊徐,今年我18啦。
    ---------------------------------------------------------------------------
    TypeError Traceback (most recent call last)
    <ipython-input-21-2a30cd8f06c0> in <module>
    3 say_hello('小钱',age=18)
    4 say_hello(name='啊徐',age=18)
    ----> 5 say_hello('老吴',18) TypeError: say_hello() takes 1 positional argument but 2 were given

混合

  • 官方的定义:https://peps.python.org/pep-0570/

    def func(positional_only_parameters, /, positional_or_keyword_parameters,
    *, keyword_only_parameters):
    pass
  • 混合后有些定义跟普通的函数定义方式略有调整。

    # 对的
    def func1(p1, p2, /, p_or_kw, *, kw):
    pass
    def func2(p1, p2=None, /, p_or_kw=None, *, kw):
    pass
    def func3(p1, p2=None, /, *, kw): # 这个可能是最容易 让人误判的
    pass
    def func4(p1, p2=None, /):
    pass
    def func5(p1, p2, /, p_or_kw):
    pass
    def func6(p1, p2, /):
    pass
    def func7(p_or_kw, *, kw):
    pass
    def func8(*, kw):
    pass
  • 以上都是合法的,以下就是不合法的

    # 错的
    def fun9(p1, p2=None, /, p_or_kw, *, kw): # 错在 p_or_kw
    pass
    def fun10(p1=None, p2, /, p_or_kw=None, *, kw): # 错在 p1和 p2,
    pass
    def fun11(p1=None, p2, /):
    pass

好处?用途?

  • 这么做的好处是啥呢?

  • 将形参标记为仅限位置形参将允许在未来修改形参名而不会破坏客户的代码。

  • 示例1

    def say_hello(name,/,age=18):
    print(f'你好!我是{name},今年我{age}啦。') say_hello('wu',18) #通常你会这样调用
    # 以后,你觉得name不合适,改为xingming
    def say_hello(xingming,/,age=18):
    print(f'你好!我是{xingming},今年我{age}啦。') say_hello('wu',18) # 你的调用仍然可以正常工作

  • 示例2:来看一个函数定义

    def foo(name, **kwds):
    print(f'My name is : {name} ')
    return 'name' in kwds foo('wuxianfeng',name='zhangsan') # 本意是输出用户名,看看是否提供关键字参数name
    TypeError                                 Traceback (most recent call last)
    <ipython-input-33-07a88704ecfd> in <module>
    4
    5
    ----> 6 foo('wuxianfeng',name='zhangsan') TypeError: foo() got multiple values for argument 'name'
  • 这样改下即可。此name非彼name,name好像可以使用两次

    def foo(name, /,**kwds):
    print(f'My name is : {name} ')
    return 'name' in kwds foo('wuxianfeng',name='zhangsan')
  •  

Python中的函数定义中的斜杠/和星号*的更多相关文章

  1. [Python]Python Class 中的 函数定义中的 self

    In [80]: class MyClass001: ....: def selfDemo(self): ....: print 'My Demo' ....: In [81]: p = MyClas ...

  2. python开发_python中的函数定义

    下面是我做的几个用列: #python中的函数定义,使用和传参 def_str = '''\ python中的函数以如下形式声明: def 函数名称([参数1,参数2,参数3......]): 执行语 ...

  3. python 可变参数函数定义* args和**kwargs的用法

    python函数可变参数 (Variable Argument) 的方法:使用*args和**kwargs语法.其中,*args是可变的positional arguments列表,**kwargs是 ...

  4. python学习7—函数定义、参数、递归、作用域、匿名函数以及函数式编程

    python学习7—函数定义.参数.递归.作用域.匿名函数以及函数式编程 1. 函数定义 def test(x) # discription y = 2 * x return y 返回一个值,则返回原 ...

  5. 第7.23节 Python使用property函数定义属性简化属性访问的代码实现

    第7.23节 Python使用property函数定义属性简化属性访问的代码实现 一.    背景       在本章前面章节中,我们介绍了类相关的知识,并举例进行了说明,在这些例子中会定义一些形如 ...

  6. Python使用property函数定义的属性名与其他实例变量重名会怎么样?

    首先如果定义的属性名与该属性对应的操作方法操作的实例对象同名就会触发无穷的递归调用,相关部分请参考<Python案例详解:使用property函数定义与实例变量同名的属性会怎样?> 但如果 ...

  7. Python使用property函数定义属性访问方法如果不定义fget会怎么样?

    我们知道Python使用property函数定义属性访问方法时的语法如下: 实例属性=property(fget=None, fset=None, fdel=None, doc=None) 而是要@p ...

  8. python简单的函数定义和用法实例

    python简单的函数定义和用法实例 这篇文章主要介绍了python简单的函数定义和用法,实例分析了Python自定义函数及其使用方法,具有一定参考借鉴价值,需要的朋友可以参考下 具体分析如下: 这里 ...

  9. python中的函数(定义、多个返回值、默认参数、参数组)

    函数定义 在python中函数的定义以及调用如下代码所示: def test(x): y = x+1 return y result = test(2) print(result) 多个返回值的情况 ...

  10. python的函数定义中99%的人会遇到的一个坑

    列表是一种经常使用的数据类型.在函数的定义中,常常会使用列表作为参数. 比如,要测试一个接口的数据,接口返回的数据格式如下: { "code": "20000" ...

随机推荐

  1. PHP实现CURL发送请求

    public function curl($url, $params = false, $ispost = 0) { $httpInfo = array(); //初始化 $ch = curl_ini ...

  2. C#winform使用NOPI读取Excel读取图片

    需求:在Winform使用NOPI做导入时候,需要导入数据的同时导入图片. 虽然代码方面不适用(我好像也没仔细看过代码),但是感谢大佬给了灵感http://www.wjhsh.net/IT-Ramon ...

  3. NOI2011真题:兔兔与蛋蛋游戏

    NOI2011真题:兔兔与蛋蛋游戏 题目描述 这些天,兔兔和蛋蛋喜欢上了一种新的棋类游戏. 这个游戏是在一个 n行 m 列的棋盘上进行的.游戏开始之前,棋盘上有一个格子是空的,其它的格子中都放置了一枚 ...

  4. 【Devexpres】spreadsheetControl自动列宽

    Worksheet worksheet = this.spreadsheetControl1.ActiveWorksheet; worksheet.Import(datatable, true, 0, ...

  5. jmeter 从多个数中随机取一个值的方法

    问题描述:使用jmeter进行接口测试时,遇到枚举值(如:10代表闲置.15代表使用中.20代表维修等)我们需要随机取一个类型传到接口中. 解决思路:通过函数助手查找随机函数,找到__chooseRa ...

  6. 用openpyxl创建工作簿和工作表

    import osimport openpyxl #设置默认路径os.chdir(r'D:/openpyxl/') #创建工作簿变量 wb = openpyxl.Workbook() #创建工作表变量 ...

  7. 第三模块的下载、requests模块、openpyxl模块

    目录 第三方模块的下载安装 下载第三模块的方式 针对下载第三模块时可能会出现的问题 网络爬虫模块之requests模块 自动化办公领域之openpyxl模块 第三方模块的下载安装 第三方模块:别人写的 ...

  8. C++进阶(map+set容器模拟实现)

    关联式容器 关联式容器也是用来存储数据的,与序列式容器(如vector.list等)不同的是,其里面存储的是<key,value>结构的键值对,在数据检索时比序列式容器效率更高.今天要介绍 ...

  9. 8、ThreadPoolTaskExecutor线程并发

    一.线程池的优点: 1.降低资源消耗.通过重复利用自己创建的线程降低线程创建和销毁造成的消耗. 2.提高响应速度.当任务到达时,任务可以不需要等到线程创建就能立即执行. 3.提高线程的可管理性.线程是 ...

  10. 2022年7月13日,第四组 周鹏 JAVA认识的第一天,附加一个用JS写的计算器代码

    心情:╭(╯^╰)╮ ╮(╯﹏╰)╭ (╯﹏╰)b 罒ω罒 |*´Å`)ノ ( Ĭ ^ Ĭ ) (ㄒoㄒ) o(╥﹏╥)o /(ㄒoㄒ)/~~ (〒︿〒) ┭┮﹏┭┮ ε(┬┬﹏┬┬)3 ε(┬┬﹏┬ ...