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
justdoit123
V2EX  ›  Python

吐槽 Python 的 *args, **kwargs

  •  3
     
  •   justdoit123 · 77 天前 · 3982 次点击
    这是一个创建于 77 天前的主题,其中的信息可能已经有所发展或是发生改变。
    接手一个数据拷贝的任务,在老代码里看到大量 def xxx_fn(*args, **kwargs) 真的血压升高。

    这两兄弟里面“什么都有,又什么都没有“,反正全靠猜。

    没有注释,就算有注释,随时时间迁移也未必准确。

    从最外层到最内层,每一层都有可能往 kwargs 里塞参数或者 pop 参数。

    这样的代码心智负担大,理解起来效率低。

    这种写法一点也不酷,真的要慎重是用。另外要吐槽,python 社区还有大量这种库(包括官方自带的库),不过幸好质量好一点库都有参数注释,而且(应该是)有持续维护。


    我也在思考,为什么各类语言要有那么多酷炫无比的特性?我认为,这些特性大部分是为基础库服务的。上层逻辑代码乱用这种特性,只会给自己找麻烦。


    一下省略 “*args, **kwargs“ 个字
    49 条回复    2024-10-17 10:42:49 +08:00
    ipwx
        1
    ipwx  
       77 天前
    因为好的库不用这个,反而 Annotation 用的多。
    justdoit123
        2
    justdoit123  
    OP
       77 天前
    麻蛋,看这种代码感觉自己是在 剥洋葱/捉迷藏。

    十年前的 coder 说:“大王,来抓我呀~ ” “我在这,来抓我呀~”。
    summerwar
        3
    summerwar  
       77 天前   ❤️ 1
    基础性的方法不限制传入的参数的数量和种类,可以更方便的处理各种数据,十分灵活。如果你要限定传入的参数和内容,那么可以在基础方法的基础上再定义函数和具体化参数。

    *args 表示传入的是列表或元祖,*kwargs 传递的是字典,记住这两条就问题不大了,按照这个规则将获取到的参数放入自己的方法里,不在自己方法里的参数直接丢掉就好了。
    mark2025
        4
    mark2025  
       77 天前
    动态一时爽,维护 xxx 。 动态还是 TS 最爽,兼顾 js 的灵活以及类型保护
    shylockhg
        5
    shylockhg  
       77 天前
    这玩意我只记得以前弄懂过一次,现在又忘完了
    iorilu
        6
    iorilu  
       77 天前
    python 确实很多地方用这个, 其实主要就是为了兼容未来可能增加得参数, 这样以后
    随便传啥, 反正接口不用改
    ounxnpz
        7
    ounxnpz  
       77 天前
    如果你想写个通用一点的装饰器,没这两个还真的不方便。这两个参数用于传递很好用,不要滥用就好
    yohole
        8
    yohole  
       77 天前   ❤️ 2
    这是 javaer 学 python 最难受的一点
    git00ll
        9
    git00ll  
       77 天前
    是的很难受,特别是调用第三方接口时,压根不知道往里面传什么
    iorilu
        10
    iorilu  
       77 天前
    @ounxnpz 是的, 装饰器必须用这个兼容所有接口得

    所以说, python 很多所谓技巧, 其实不是技巧, 而是刚需

    你可以不用, 但你必须知道
    Binwalker
        11
    Binwalker  
       77 天前
    ruby 里面也有,而且比这个还自由
    fatigue
        12
    fatigue  
       77 天前   ❤️ 1
    工程设计的锅东西别让语言特性来背
    Jinnrry
        13
    Jinnrry  
       77 天前   ❤️ 5
    和语言没啥关系,和人有关系。
    难道 php 就不能一个 array 满天飞了吗?
    难道 java 就不能一个 Object 满天飞了吗?
    难道 golang 就不能一个 any 满天飞了吗?


    不过有一说一,接手 python 代码我是最害怕的,1 个项目 10 个人有 100 种写法,每次都能学到新姿势。python 天天喊着“人生苦短”,就这 100 种写法,看下来确实人生苦短了
    TimG
        14
    TimG  
       77 天前 via Android
    @Jinnrry 不能这么说,还是跟语言有关系的,python 显然有支持这种行为的意图,现在都成为一种标准了。Java 要是装箱不用拆我也用,但它是强类型,不光写一堆拆箱还要折算性能损耗,合计下来就很不经济。
    DOLLOR
        15
    DOLLOR  
       77 天前
    有类型标注还好,如果没类型标注,维护 python 就是痛苦的折磨
    14
        16
    14  
       77 天前
    同感,我最近喜欢 def func(*, a, b=2, c) 这样定义,这样调用的时候必须 func(a=1, c=3) 强制写清楚参数名字,并且 abc 可以在任意位置写默认值
    buf1024
        17
    buf1024  
       77 天前   ❤️ 2
    防御性编程,值得点赞。
    ClericPy
        18
    ClericPy  
       77 天前
    老代码让我用 type-hints 给包了一层 interface 。。。

    不敢动底层啊,上次动了,加班到晚上 9 点多!
    est
        19
    est  
       77 天前
    数据拷贝任务你换啥语言来都是 dirty work
    bhy
        20
    bhy  
       77 天前
    可以试试看 pytype 能不能 infer kwargs 的类型。看了一圈,mypy 和 pyright 应该还不支持。
    miaotaizi
        21
    miaotaizi  
       77 天前
    "屎中一坨", 别说什么 “人生苦短”, 不接受反驳
    NoOneNoBody
        22
    NoOneNoBody  
       77 天前
    params = ((1,2,3), (4,5,6,7), ...)
    result = 0
    for i,fun in enumerate((fun1, fun2, fun3, ...)): result=fun(result, *params[i])
    print(result)

    有些时候,上述 1,2,3 或 4,5,6,7 这些参数就是从上游得到的结果,只需按需顺列好传到下游计算,不需要理会其具体意义,这时候,args 就很有用了
    例如上述代码写成闭包,只需传 params 和 result 初始值,就能完成一长串的计算,不需要记太多参数意义,可能更重要的点是 fun1/fun2/fun3...的顺序

    数据预处理、标准化经常就是这样枯燥的,步骤和参数固定,机械化按顺序执行就是了
    爬虫也是,无非就是 bs4+selector ,re+pattern ,lxml+xpath ,函数形式基本固定,变化只是 selector/pattern/xpath 这些,bs4/re/lxml 谁先谁后可能更重要

    其实还有其他用法,如参数“补齐”
    有十个参数,参数名都是固定的,有若干个 fun 都用这些同名参数,fun1 只用到 3 个,fun2 只用到 7 个……,可以全部十个都用 kwargs 传进去,传多了也不会错,不用逐个 fun 检查并选择哪些参数。例如处理图片,很多函数都是用相同的参数名,其意义也固定的,什么宽高、通道数……如果已知足够多参数,一起用 kwargs 传过去也不会错的,除非这个用 w/h 表示宽高,那个用 width/height 表示宽高,这就麻烦了
    miaotaizi
        23
    miaotaizi  
       77 天前
    @NoOneNoBody 为什么不把你的 10 个参数 做成一个对象?
    winterbells
        24
    winterbells  
       77 天前
    更难受的是查找调用,结果弹出几十个无关路径,仅仅因为名字一样==
    NoOneNoBody
        25
    NoOneNoBody  
       77 天前
    @miaotaizi #23
    也可以啊,dataclass 就是这个用途吧,只是我没参透

    def fun1(width, height): ...
    def fun2(a, width,height): ...

    params = {'width': 1920, 'height':1080}

    fun1(**params)
    a = 123
    fun2(a, **params)
    GeekGao
        26
    GeekGao  
       77 天前
    语法糖能取代 BOB 大爷的设计模式吗 LMAO
    至今为止尚未有语言可以做到吧。所以也别吐槽灵活性设计了。
    justdoit123
        27
    justdoit123  
    OP
       77 天前
    没有攻击语言的意思。 估计这也是工程演进的产物。

    也不是只是单纯的吐槽。 真的建议不要乱用语言特性。
    liprais
        28
    liprais  
       77 天前
    你都用 python 了还纠结这些
    genesislive
        29
    genesislive  
       77 天前
    matplotlib 里面应该有不少
    mywaiting
        30
    mywaiting  
       77 天前
    别人写的 *args **kwargs 什么垃圾,类型标识都没有,简直屎山! python 垃圾语言!
    自己一把梭 *args **kwargs 内部各种对 kwargs 一会 pop 一会 setdefault 真香!干净简洁优雅! python 牛逼!
    adoal
        31
    adoal  
       77 天前   ❤️ 1
    用来做透传很方便
    Vonrix
        32
    Vonrix  
       77 天前
    每次用完就忘
    ddkk1112
        33
    ddkk1112  
       77 天前
    许多第三方包特别喜欢用这两玩意
    调用还得看一大堆文档,操
    ytmsdy
        34
    ytmsdy  
       77 天前
    @Jinnrry 哈哈哈哈,确实,Python 里面的花式语法确实是最多的!就算 Python 的老司机,看到有些花式语法也要愣一下。
    CodeCodeStudy
        35
    CodeCodeStudy  
       77 天前
    @Jinnrry #13 golang 没有 any ,那是 typescript ,golang 的是 interface{}
    InkStone
        36
    InkStone  
       77 天前
    也没啥好吐槽的,所有现存的静态类型语言都有相同的机制,更不要说动态类型的了。

    C 里有 void*满天飞的代码,Java 也有很多人喜欢传 Map ,Golang interface{} 一把梭,Typescript 可以用 any 退化成多写几个字母的 javascript……
    qq135449773
        37
    qq135449773  
       77 天前
    这东西只能说仁者见仁智者见智了,IDE 静态分析足够强的话感觉问题还不算大
    qW7bo2FbzbC0
        38
    qW7bo2FbzbC0  
       77 天前
    @CodeCodeStudy 新版本里面针对泛型类型指定,的确是 any 更符合语义,interface{}是 1.8 之前的用法了
    lolizeppelin
        39
    lolizeppelin  
       77 天前
    不能喷公司垃圾代码 转头喷 python 提供的功能 2333
    lolizeppelin
        40
    lolizeppelin  
       77 天前
    @ipwx
    兄弟你论坛强度有点高啊..........感觉哪都能看到你 2333333333
    真羡慕你啊...有那么多时间刷论坛 2333
    vialon17
        41
    vialon17  
       77 天前
    滥用想想都脑壳大,没有类型注释不麻烦吗?
    uni
        42
    uni  
       77 天前
    直接在 linter 里面禁用这些东西
    每次跟别人合作 py 代码我都会要求他们把 vscode 的 py 类型检查(默认是关闭的)打开,他们可以不改但是编辑器报满屏都是红色的他们要能看到
    chashao
        43
    chashao  
       77 天前
    这个真他妈恶心,重构火葬场
    ipwx
        44
    ipwx  
       76 天前
    @lolizeppelin 啊哈哈哈,大概是因为我喜欢在没人回复的帖子下面先回复,如果有很多回复的帖子我反而不会回复也不会看回复吧。所以你经常在前几楼看见我,其实我水的不多。
    lyxxxh2
        45
    lyxxxh2  
       76 天前
    ```
    def init_tracker(self, *args):
    self.tracker = CashTracker(*args)
    return self
    ```
    参数多了,挺方便啊。
    Rehtt
        46
    Rehtt  
       76 天前 via Android
    @CodeCodeStudy 1.18 以后就有了
    // any is an alias for interface{} and is equivalent to interface{} in all ways.
    type any = interface{}
    CodeCodeStudy
        47
    CodeCodeStudy  
       76 天前
    @qW7bo2FbzbC0 38
    @Rehtt #46
    感谢指出,有一段时间没看 golang 了
    xiebow
        48
    xiebow  
       74 天前
    @mywaiting 确实
    qq78660651
        49
    qq78660651  
       71 天前
    写 python 就算是自己维护,不写注释,2 周后我都不知道里面我写的啥。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5730 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 06:26 · PVG 14:26 · LAX 22:26 · JFK 01:26
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.