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 · 4 天前 · 3153 次点击
    接手一个数据拷贝的任务,在老代码里看到大量 def xxx_fn(*args, **kwargs) 真的血压升高。

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

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

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

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

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


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


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

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

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

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

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


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

    不敢动底层啊,上次动了,加班到晚上 9 点多!
    est
        19
    est  
       4 天前
    数据拷贝任务你换啥语言来都是 dirty work
    bhy
        20
    bhy  
       4 天前
    可以试试看 pytype 能不能 infer kwargs 的类型。看了一圈,mypy 和 pyright 应该还不支持。
    miaotaizi
        21
    miaotaizi  
       4 天前
    "屎中一坨", 别说什么 “人生苦短”, 不接受反驳
    NoOneNoBody
        22
    NoOneNoBody  
       4 天前
    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  
       4 天前
    @NoOneNoBody 为什么不把你的 10 个参数 做成一个对象?
    winterbells
        24
    winterbells  
       4 天前
    更难受的是查找调用,结果弹出几十个无关路径,仅仅因为名字一样==
    NoOneNoBody
        25
    NoOneNoBody  
       4 天前
    @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  
       4 天前
    语法糖能取代 BOB 大爷的设计模式吗 LMAO
    至今为止尚未有语言可以做到吧。所以也别吐槽灵活性设计了。
    justdoit123
        27
    justdoit123  
    OP
       4 天前
    没有攻击语言的意思。 估计这也是工程演进的产物。

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

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