V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
shawndev
V2EX  ›  Python

悉数 Python 函数传参的语法糖

  •  
  •   shawndev · 2020-11-02 11:20:18 +08:00 · 1846 次点击
    这是一个创建于 1242 天前的主题,其中的信息可能已经有所发展或是发生改变。

    TIOBE 排行榜是程序开发语言的流行使用程度的有效指标,对世界范围内开发语言的走势具有重要参考意义。随着数据挖掘、机器学习和人工智能相关概念的风行,Python 一举收获 2018 年年度语言,这也是 Python 继 2007 和 2010 年之后第三次问鼎榜单。前阵子在 GitHub 创建 Profile README 时惊觉 Python 是我的第二高频语言,回想第一次见到 positional only parameter 传参方式时一头雾水的查找资料,借由周末闲暇,将 Python 函数传参的部分语法糖做一次总结。

    Python 是 Guido van Rossum (以下简称 GvR )在 1989 年圣诞节创造的编程语言,支持多种编程范式,1991 年对外发布了第一个版本。随后的 2000 年发布了 Python2,2008 年发布了不完全向前兼容的 Python3,Python 语言目前由 Python 基金会进行维护,2020 年 1 月 1 日 Python2 正式落幕,因此本文所述内容均基于 Python3 。

    在 Python 中将面向过程方式声明的函数(即不归属于类)称为函数( function ),而面向对象方式声明的函数称为方法( method )。

    语法糖 1:

    实例方法第一个参数默认为当前实例对象,类方法第一个参数默认为当前类对象,静态方法没有该约束。

    # 函数
    def greeting():
      print("function")
    
    class Human:
      # 实例方法
      def greeting(self):
        print("instance method")
      
      # 类方法
      @classmethod
      def write(cls):
        print("class method")
      
      # 静态方法  
      @staticmethod
      def read():
        print("static method")
    

    语法糖 2:

    参数可以具备默认值,对比 Objective-C 由于这项特性的缺失需要实现构造方法和便利构造方法。

    def greeting(name = "Jack Ma"):
      print("Hello", name)
      
    >>> greeting()
    Hello Jack Ma
    >>> greeting("Pony Ma")
    Hello Pony Ma
    

    语法糖 3:

    通过*args 表示可变参数。

    def calc(name, *args):
      spent = 0
      for v in args:
        spent += v
      print(name, "spent money:", spent)
      
    >>> calc("Jack Ma", 1000000, 2000000)
    Jack Ma spent money: 3000000
    

    传入的参数通过 type(args)可以检查类型为元组( tuple ),除了函数声明外函数调用亦然。

    >>> args = (1000000, 2000000, 3000000) # [1, 2, 3]同理
    >>> calc("Jack Ma", *args)
    Jack Ma spent money: 6000000
    

    通过**kwargs 以键值对的形式表示可变参数。

    def calc(name, **kwargs):
      spent = name + " spent on:"
      for k, v in kwargs.items():
        spent += "%s=%s," % (k, v)
      print(spent)
    
    >>> calc("Jack Ma", eating=10000, shopping=20000)
    Jack Ma spent on:eating=10000,shopping=20000
    

    同理,函数调用亦然。

    >>> kwargs = {"eating":10000, "shopping":20000}
    >>> calc("Jack Ma", **kwargs)
    Jack Ma spent on:eating=10000,shopping=20000
    

    自 Python1 起就支持通过位置和参数名称两种方式传递参数,如对于原型为 def add(x, y)的函数,同时支持以下两种调用方式。

    add(1, 2)
    add(x=1, y=2)
    

    语法糖 4:

    Keyword-Only Arguments (PEP-3012)

    函数声明时,参数列表中单独的*表示在此之后的参数仅支持通过名称进行传递。通过这个语法可以在可变参数列表之后传递其他参数。

    def compare(a, b, *, key=None):
      pass
      
    >>> compare(1, 2, 3) # 调用失败
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: compare() takes 2 positional arguments but 3 were given
    >>> compare(1, 2, key=3) # 调用成功
    

    语法糖 5:

    Positional-Only Parameters (PEP-570)

    函数声明时,参数列表中单独的 /表示在此之前的参数仅支持通过位置进行传递。这个语法主要解决部分函数的参数没有实际意义,后续可能会进行修改,避免通过名称传递参数在名称修改后失效。比如 int(x="1")中 x 并没有实际意义,而且相比 int("1")可读性有所不足。

    def name(positional_only_parameters, /, positional_or_keyword_parameters,
             *, keyword_only_parameters):
      pass
    

    此外,官方文档表述这个语法还解决了某个边界条件下的调用失败。对于以下函数:

    def foo(name, **kwargs):
        return 'name' in kwargs
    

    当我们传递的 kwargs 拥有一个"name"的 key 时会调用失败,如下。

    >>> foo(1, **{'name': 2})
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: foo() got multiple values for argument 'name'
    

    通过以下方式可以解决这个问题。

    def foo(name, /, **kwargs):
        return 'name' in kwargs
    

    语法糖 6:

    如何修改参数的引用,官方文档解释称:Python 中参数的传递通过值传递(但引用是通过值传递的,参考链接 2 ),最简单的方式是将入参按顺序以元组进行返回。

    语法糖 7:

    由于 Python 的 first-class function 特性,函数作为一等公民可以像其他类型一样进行传递和赋值。比如 max 函数原型可以传入一个用于比较的函数,当然函数也可以被重新赋值。

    >>> max=min
    >>> max(19,9)
    9
    

    全局函数和关键字有时候会不易分辨,比如 print 在 Python2 中是关键字,而在 Python3 中是内置函数。可以通过下列方式获取关键字列表。

    >>> import keyword
    >>> keyword.kwlist
    ['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']
    

    语法糖 8:

    类型标注( Type Hint )通过:type 表示参数类型,由于 Python 动态类型的特性 Python 运行时并不强制标注函数和变量类型,类型标注可被用于 IDE 等第三方工具。如下:

    def greeting(name: str) -> str:
        return 'Hello ' + name
    

    详细内容可以参考官方中文文档:

    回想第一次在 Python 作者 GvR 的 500-lines-of-code 中见到 Keyword-Only Arguments 的用法几度无法理解代码为什么能够正常运行,顺便一提,该项目同时也是绝佳的编程学习材料。

    行文至此语法糖的数量超出了我的预期,本篇文章创作期间我惊喜的发现官方已经宣告了 Python2 的落幕,官方文档提供了简体中文版本,而 Python 也已经发布了 3.9 版本,被称为 BDFL 的 GvR 也在 2018 年通过邮件组宣布了权力的交接。

    通过 TIOBE 长期榜单变化可以发现,Python 作为这个榜单的常青树且仍然保持着上升趋势,身边也有不少人自学这门语言。文章的最后摘录 Python 之禅与各位共勉。

    wechat_banner.png

    >>> python3
    >>> import this
    The Zen of Python, by Tim Peters
    
    Beautiful is better than ugly.
    Explicit is better than implicit.
    Simple is better than complex.
    Complex is better than complicated.
    Flat is better than nested.
    Sparse is better than dense.
    Readability counts.
    Special cases aren't special enough to break the rules.
    Although practicality beats purity.
    Errors should never pass silently.
    Unless explicitly silenced.
    In the face of ambiguity, refuse the temptation to guess.
    There should be one-- and preferably only one --obvious way to do it.
    Although that way may not be obvious at first unless you're Dutch.
    Now is better than never.
    Although never is often better than *right* now.
    If the implementation is hard to explain, it's a bad idea.
    If the implementation is easy to explain, it may be a good idea.
    Namespaces are one honking great idea -- let's do more of those!
    
    8 条回复    2020-11-02 19:22:36 +08:00
    shawndev
        1
    shawndev  
    OP
       2020-11-02 11:22:26 +08:00
    创作不易,如果内容对你有帮助,欢迎关注我的个人公众号晨晓(chenxiaopost)或留下你的评论。致力于成为一个也许有料也有趣的创作者,写代码,也写文字。
    GeekBao
        2
    GeekBao  
       2020-11-02 12:33:53 +08:00
    谢谢分享, 已关注.
    居然是郑州的朋友.
    shawndev
        3
    shawndev  
    OP
       2020-11-02 12:59:04 +08:00
    @GeekBao 谢谢评论,居然是郑州的朋友(互联网荒漠名不虚传
    no1xsyzy
        4
    no1xsyzy  
       2020-11-02 13:05:59 +08:00
    1 4 5 是语法盐
    7 与语法无关
    binux
        5
    binux  
       2020-11-02 13:08:45 +08:00 via Android
    你是不是对语法糖这个词有什么误解?
    shawndev
        6
    shawndev  
    OP
       2020-11-02 13:09:11 +08:00
    @no1xsyzy 是的,因为 python 是一个凝练的语言,所以其他语言也许不具备的 tricky 用法我都按照语法糖进行归类了。感谢您的指点。
    shawndev
        7
    shawndev  
    OP
       2020-11-02 13:13:08 +08:00
    @binux #6 的评论能否解答您的问题。
    deplives
        8
    deplives  
       2020-11-02 19:22:36 +08:00
    我觉得你对 "语法糖" 这个概念有点误解,楼上已经有人说到了,但你的回答核心在于 "自己总结了一套所谓的语法糖的概念"。 你可以去 wiki 上搜一下 Syntactic_sugar 了解下啥叫语法糖
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   961 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 21:46 · PVG 05:46 · LAX 14:46 · JFK 17:46
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.