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

len(x) 击败 x.len(),从内置函数看 Python 的设计思想(内有公号宣传,不喜勿进)

  •  
  •   chinesehuazhou · 2019-04-21 20:44:05 +08:00 · 8277 次点击
    这是一个创建于 1004 天前的主题,其中的信息可能已经有所发展或是发生改变。

    内置函数是 Python 的一大特色,用极简的语法实现很多常用的操作。

    它们预先定义在内置命名空间中,开箱即用,所见即所得。Python 被公认是一种新手友好型的语言,这种说法能够成立,内置函数在其中起到了极关键的作用。

    举个例子,求字符串 x 的长度,Python 的写法是 len(x) ,而且这种写法对列表、元组和字典等对象也同样适用,只需要传入对应的参数即可。len() 函数是共用的。

    这是一种极简哲学的体现:Simple is better than complex。

    但是,有些语言并不是这样,例如在 Java 中,字符串类有一个求长度的方法,其它类也有自己的求长度的方法,它们无法共用。每次使用时,通过类或实例来调用。

    同样是求字符串长度,Python 的写法:

    saying = "Hello world!"
    print(len(saying))
    
    # 结果:12
    

    而在 Java 中,写法可能如下(简化起见):

    String saying = "Hello world!";
    System.out.println(saying.length());
    
    // 结果:12
    

    Python 采用的是一种前缀表达式 ,而 Java 采用的则是后缀表达式

    除了求长度,Python 的某些内置函数也能在 Java 中找到对应的表达。例如,数值型字符串 s 转化为整型数字,Python 可以用 int(s) 函数,而 Java 可以用 Integer.parseInt(s) ;整型数字转化为字符串,Python 可以用 str(i) ,而 Java 也有 String.valueOf(i)

    Python 的内置函数不与特定的类绑定,它们是一级对象。而 Java 的“函数”则无法脱离类而存在,它们只是附属品。

    从直观角度来看,Python 的表达似乎是更优的。但是,它们并不具有可比性 ,因为这是两套语言系统,各有独特的范畴背景,并不能轻易地化约。

    就好比是,不能因为拉丁字母笔画简单,就说它优于汉字,因为在表意时,字母(表音文字)是远逊于汉字(表意文字)的。同样的,日本借用了汉字的偏旁部首而造出来的文字,虽然更省笔墨,但是也完全丧失了意蕴。

    以此类比,Python 的内置函数虽有简便之美,但却丢失了某些表意功能。有些人在质疑 /抨击 Python 的时候,也喜欢拿这点说事,认为这是 Python 的设计缺陷。

    这就引出本文最想讨论的一个问题来:为什么 Python 要设计成 len(x) 这种前缀表达,而不是 x.len() 这样的后缀表达呢?

    事实上,后缀设计也是可行的,以 Python 中列表的两个方法为例:

    mylist = [2, 1, 3, 5, 4]
    
    mylist.sort()
    print(mylist)   # [1, 2, 3, 4, 5]
    
    mylist.reverse()
    print(mylist)   # [5, 4, 3, 2, 1]
    

    它们都是通过列表对象来调用,并不是凭空从内置命名空间中拿来的。语义表达得也很清楚,就是对 mylist 做排序和逆转。

    恰恰那么巧,它们还有两个同父异母的兄弟 sorted() 与 reversed(),这俩是前缀表达型。

    mylist = [2, 1, 3, 5, 4]
    
    sort_list = sorted(mylist)
    print(sort_list)   # [1, 2, 3, 4, 5]
    
    reverse_list = reversed(mylist)
    print(list(reverse_list))   # [4, 5, 3, 1, 2]
    

    不同的写法,都在做同一件事(不考虑它们的副作用)。因此,后缀语法并非不可行,之所以不用,那肯定是刻意的设计。

    回到前面的问题:为什么是 len(x) ,而不是 x.len(x),这根源于 Python 的什么设计思想呢?

    Python 之父 Guido van Rossum 曾经解释过这个问题(链接见文末),有两个原因:

    • 对于某些操作,前缀符比后缀更好读——前缀(和中缀)表示法在数学中有着悠久的历史,其视觉效果有助于数学家思考问题。我们可以简单地把公式 x*(a + b) 重写成 x*a + x*b ,但同样的事,以原生的面向对象的方式实现,就比较笨拙。
    • 当读到 len(x) 时,我就 知道 这是在求某对象的长度。它告诉我了两点:返回值是一个整数,参数是某种容器。但当读到 x.len() 时,我必须事先知道某种容器 x,它实现了一个接口,或者继承了一个拥有标准 len() 方法的类。我们经常会目睹到这种混乱:一个类并没有实现映射( mapping )接口,却拥有 get() 或 keys() 方法,或者某些非文件对象,却拥有一个 write() 方法。

    解释完这两个原因之后,Guido 还总结成一句话说:“ I see 'len' as a built-in operation ”。这已经不仅是在说 len() 更可读易懂了,而完全是在拔高 len() 的地位。

    这就好比说,分数 ½ 中的横线是数学中的一个“内置”表达式,并不需要再实现什么接口之类的,它自身已经表明了“某数除以某数 ”的意思。不同类型的数(整数、浮点数、有理数、无理数...)共用同一个操作符,不必为每类数据实现一种求分数的操作。

    优雅易懂是 Python 奉行的设计哲学 ,len() 函数的前缀表达方式是最好的体现。我想起在《超强汇总:学习 Python 列表,只需这篇文章就够了》这篇文章中,曾引述过 Guido 对“为什么索引从 0 开始 ”的解释。其最重要的原因,也正是 0-based 索引最优雅易懂。

    让我们来先看看切片的用法。可能最常见的用法,就是“取前 n 位元素”或“从第 i 位索引起,取后 n 位元素”(前一种用法,实际上是 i == 起始位的特殊用法)。如果这两种用法实现时可以不在表达式中出现难看的 +1 或 -1,那将会非常的优雅。

    使用 0-based 的索引方式、半开区间切片和缺省匹配区间的话( Python 最终采用这样的方式),上面两种情形的切片语法就变得非常漂亮:a[:n] 和 a[i:i+n],前者是 a[0:n] 的缩略写法。

    所以,我们能说 len(x) 击败 x.len() ,支撑它的是一种化繁为简、纯粹却深邃的设计思想。

    面向对象的编程语言自发明时起,就想模拟我们生活于其中的现实世界。可是什么类啊、接口啊、对象啊、以及它们的方法啊,这些玩意的毒,有时候蒙蔽了我们去看见世界本质的眼睛。

    桌子类有桌子类的求长度方法,椅子类有椅子类的求长度方法,无穷无尽,可现实真是如此么?求长度的方法就不能是一种独立存在的对象么?它之所以存在,是因为有“对象”存在,而不是因为有某个类才存在啊。

    所以,我想说,len(x) 击败 x.len(),这还体现了 Python 对世界本质的洞察

    求某个对象的长度,这种操作独立于对象之外而存在,并不是该对象内部所有的一种属性或功能。从这个角度理解,我们能够明白,为什么 Python 要设计出内置函数? 内置函数其实是对世界本质的一种捕捉。

    这些见微知著的发现,足够使我们爱上这门语言了。人生苦短,我用 Python。

    关联阅读:

    Guido 解释 len 的由来: https://mail.python.org/pipermail/python-3000/2006-November/004643.html

    Guido 解释 0 索引的由来: https://python-history.blogspot.com/2013/10/why-python-uses-0-based-indexing.html

    本文原创并首发于公众号 [Python 猫] ,未经授权,请勿转载。

    原文地址: https://mp.weixin.qq.com/s/pKQT5wvyaSNFvnJexiCC8w

    公众号 [Python 猫] , 本号连载优质的系列文章,有喵星哲学猫系列、Python 进阶系列、好书推荐系列、技术写作、优质英文推荐与翻译等等,欢迎关注哦。后台回复“爱学习”,免费获得一份学习大礼包。

    103 条回复    2019-04-24 19:34:09 +08:00
    1  2  
    ryd994
        1
    ryd994  
       2019-04-21 21:38:57 +08:00 via Android   ❤️ 7
    作为 C 程序员,我只能说,你们这些有对象的,屁事真多
    怎么不说明白 len 只不过是个语法糖,实际上调用的是__len__
    chinesehuazhou
        2
    chinesehuazhou  
    OP
       2019-04-21 22:20:05 +08:00
    @ryd994 实际上是调的__len__,但在用 len() 的时候不需要知道,更不需要写成调用的方式。同样比如说 Java,他也可以把类似调用__len__的方法藏起来,直接用 len(),但是没有。相比较而言,Python 的这种设计就是更优雅。(糖真甜)
    cnt2ex
        3
    cnt2ex  
       2019-04-21 23:30:23 +08:00
    Guido 的两个理由根本不足以解释为什么用 len(x)而不是 x.len()
    >对于某些操作,前缀符比后缀更好读
    这一点完全和 len(x)和 x.len()之间选择无关。你只是说明了在表达*分配律*的时候用中缀比较好,与 len(x)和 x.len()之间选择有什么关系?我难道可以说,因为在表达*分配律*的时候,中缀优于前缀。所以在*写 n 个数求和*时 1+2+3+...+n 优于 sigma 求和符号?
    同样地,第二点也没法说明选择 len(x)而不是 x.len()。

    而且考虑到一致性上,获取一个对象的长度,显然 x.len()这种面向对象的风格更好。
    yujincheng08
        4
    yujincheng08  
       2019-04-21 23:36:06 +08:00 via Android   ❤️ 1
    len(x)的时候并不需要事先知道 x 是容器。所以最后程序跑着跑着突然说这句话出问题了,不能求 x 的 len。。。

    我觉得把这种意外放到运行时就很蠢。。
    9hills
        5
    9hills  
       2019-04-21 23:48:33 +08:00 via iPhone
    @yujincheng08 在 Python 里 x.len()如果没有这个方法也是运行时才能发现的,效果一样。

    动态语言就这样,没办法
    MonoLogueChi
        6
    MonoLogueChi  
       2019-04-22 00:09:13 +08:00 via Android
    桌子类有桌子类求长度的方法,椅子类有椅子类求长度的方法,那桌子椅子类都是继承物体类,物体类有求长度的方法不就可以了嘛,我个人的理解是这样的,也可能是 C#写多了,有些写法看着不习惯了
    wwqgtxx
        7
    wwqgtxx  
       2019-04-22 00:12:13 +08:00 via iPhone
    @yujincheng08 像 python 这种随时可以在运行中给对象添加一个 function 的,x.len()和 len(x)都不需要也没办法在编译时知道 x 是不是容器
    wwqgtxx
        8
    wwqgtxx  
       2019-04-22 00:20:02 +08:00 via iPhone
    @MonoLogueChi python 有种习惯叫"鸭子类型"即一个类实现了某种方法,它不需要继承某个接口也可以称为支持某操作的类型,最常见的就是 file-likes object 和 path-likes object 了,这两个在 python 官方文档中大量出现的 duck type
    这个个人感觉和 golang 的 interface 有点像,都不需要显示继承
    而 python 的 len(x)就是 x.__len__()的语法糖,只要 x 存在__len__这个函数就可以求长度,不需要继承别的语言的类似 XXXable 这种接口
    darkbread
        9
    darkbread  
       2019-04-22 00:29:40 +08:00 via iPad
    兄 dei 建议你了解下 C#的 Extention
    qiyuey
        10
    qiyuey  
       2019-04-22 00:38:08 +08:00 via Android   ❤️ 2
    有一个儿子,想找到他的爹,也有两种方法:找爹(一个儿子);一个儿子.爹()
    jinliming2
        11
    jinliming2  
       2019-04-22 01:49:27 +08:00 via iPhone
    感觉语义上的不同,len() 更像是进行测量、获取长度这个动作,而 .len 更像是读取一个已经存在自身上的一个属性,.len() 也是进行测量、获取长度这个动作。
    len(x) 和 x.len() 的区别是,一个是别人来测量自己,一个是自己测量自己。
    len(x) 没有办法预先确定要测谁的长度,所以只能每次都重新数一下。而 x.len() 就可以直接把自己的长度存起来,变化的时候改一下就行,每次返回保存的值就行。这么看来,x.len() 的设计更好。
    PY 里面的 len() 比较聪明,不会自己去数,而是调用 __len__ 直接抄答案……
    msg7086
        12
    msg7086  
       2019-04-22 04:43:34 +08:00   ❤️ 26
    看了半天感觉就是把一个很无所谓的设计强行解释出了高大上的感觉。
    原本只要对一个对象求长度,像 obj.len()这样简单的操作,特地去增加一个不伦不类的 built-in operation,让原本简单、统一的 容器.操作() 操作变成了突兀、怪异的 操作(容器) 方法,然后再强行解释说把简单的事情复杂化是为了「化繁为简」?

    那为什么要还用 list.append(item) 呢,不如花点时间扯一片文章,把 append(item, list) 当成对世界本质的洞察,然后再强迫别人去记忆这种奇怪的语法,来体现人生苦短呢。毕竟据说当你读到这样的语法的时候,「就 知道 这是」把项目放进对象里了呢。
    lilpig
        13
    lilpig  
       2019-04-22 05:07:41 +08:00 via Android
    体会不出什么别的,反正个人很不爱写 len(x)。。。😂
    fakeshadow
        14
    fakeshadow  
       2019-04-22 05:44:35 +08:00
    对世界本质的洞察。。。
    ColinZeb
        15
    ColinZeb  
       2019-04-22 07:02:30 +08:00 via iPhone
    理解不了,只感觉是尬吹。面向对象的哲学性更好,像这样的文章可以写十本书
    Cbdy
        16
    Cbdy  
       2019-04-22 07:05:58 +08:00 via Android
    恕我直言,这是个不好的设计。
    LokiSharp
        17
    LokiSharp  
       2019-04-22 07:26:38 +08:00 via iPhone
    x.len()不是挺好的么
    love
        18
    love  
       2019-04-22 07:55:40 +08:00 via Android
    函数式也有好处的,比如得到一堆长度
    array.map(len)

    不过 py 函数式并不方便,还不如 js。
    zjsxwc
        19
    zjsxwc  
       2019-04-22 07:58:30 +08:00 via Android   ❤️ 1
    强行解释,掩盖设计问题
    ChefIsAwesome
        20
    ChefIsAwesome  
       2019-04-22 08:10:08 +08:00
    大部分语言:1 + 1
    closure: + 1 1
    Govda
        21
    Govda  
       2019-04-22 08:13:46 +08:00 via Android   ❤️ 4
    所以说 PHP 对世界早已洞察的无比透彻?
    ccraohng
        22
    ccraohng  
       2019-04-22 08:19:26 +08:00 via Android
    与 php 同样的屎味?
    xuanbg
        23
    xuanbg  
       2019-04-22 08:34:36 +08:00
    好好地对象属性给搞成 Util 方法了。。。好吧,有时候 Util 挺好用,就是适用范围有限,超出范围,就会扔给你一个十分不友好的异常。
    snw
        24
    snw  
       2019-04-22 08:36:46 +08:00 via Android
    明明就是语法糖而已,看各人口味。
    snw
        25
    snw  
       2019-04-22 08:43:39 +08:00 via Android   ❤️ 1
    就我自己而言,我更喜欢统一的写法。

    你们大概没体验过 VBA 里面的混搭风格,一会儿 Len(str),一会儿 Range.Rows.Count,一会儿 UBound(arr)-LBound(arr)+1
    aijam
        26
    aijam  
       2019-04-22 08:43:40 +08:00   ❤️ 1
    Mohanson
        27
    Mohanson  
       2019-04-22 08:54:52 +08:00 via Android   ❤️ 1
    许多人被面向对象洗脑了,go, rust 里只有 struct 没有 class, 我觉得许多人应该学习一下为什么现在的新兴语言不再“面向对象”
    lihongjie0209
        28
    lihongjie0209  
       2019-04-22 08:59:46 +08:00
    @Mohanson 说的好像 C 语言没搞过这些一样?还“新兴语言”。
    C 语言时代 struct 没有解决必须用 OO 解决的问题在你所谓的“新兴语言”中解决了吗?
    Mohanson
        29
    Mohanson  
       2019-04-22 09:02:32 +08:00 via Android
    @lihongjie0209 没有必须用 oo 的场景。请举个例子 java 能实现的,但 go 实现不了的场景,否则你“必须用 OO 解决的问题在你所谓的“新兴语言”中解决了吗?”的论点不成立。
    will0404
        30
    will0404  
       2019-04-22 09:03:11 +08:00 via Android
    @Mohanson 面向对象是思想而不是 class,rust 没有 class 不代表它没有面向对象。
    lhx2008
        31
    lhx2008  
       2019-04-22 09:03:41 +08:00
    同样是 function 形式,PHP 里面 len 的函数有多少个,才能表达出 len 的多种语意?这种一刀切的方法是挺无聊的语法糖。

    C++里面的内置 function 库完全也可以搞个 len 的东西,但是并没有,还是用容器的成员函数来表示了,就是因为 len 有多个语意
    anewg
        32
    anewg  
       2019-04-22 09:06:27 +08:00
    世界本质的洞察.... 可以再吹的厉害点
    plqws
        33
    plqws  
       2019-04-22 09:07:44 +08:00
    把一个畸形设计吹上天的也就会在 python 社区里见到了
    vincel
        34
    vincel  
       2019-04-22 09:11:08 +08:00
    如果有 10 个不同的类,那么需要有 10 个 len(x),然而后缀写法只需要在类里重写,这种情况显然更优雅
    justou
        35
    justou  
       2019-04-22 09:14:48 +08:00
    我看过的说法是, python 的内置函数如__len__, __str__, __repr__等是为所有对象规定的一个 interface 或者 proxy, 如果自定义的类要实现其中某些功能, 只要在类中重写相应内置函数就可以了, 然后调用相应内置函数即可得到相应效果, 避免各种 a.size(), a.len(), a.length(), a.nitems(), a.xxx 的出现

    一句话: 提供统一的 proxy, 遵循这种统一的 proxy, pythonic ヽ(。☉౪ ⊙)ノ
    jimrok
        36
    jimrok  
       2019-04-22 09:16:02 +08:00
    len(x)是 python 的臭脚,掩盖语言设计上的随性。
    123s
        37
    123s  
       2019-04-22 09:19:14 +08:00
    是不是 oo 和函数式区别
    fakeshadow
        38
    fakeshadow  
       2019-04-22 09:24:07 +08:00
    @Mohanson go 不了解,rust 里不也是.len()来取 vec 和 str 的长度么? rust 虽然没有 class,但是 traits 和 traits object 仍然是可以写出面向对象的代码。
    Cbdy
        39
    Cbdy  
       2019-04-22 09:30:58 +08:00 via Android
    @Mohanson
    面向对象是一种思想,class 只是实践的一种方式
    OOC 了解一下
    https://book.douban.com/subject/2237446/
    douglarek
        40
    douglarek  
       2019-04-22 09:31:46 +08:00 via Android
    别扯什么前者击败后者,本质都是后者
    Mohanson
        41
    Mohanson  
       2019-04-22 09:35:37 +08:00 via Android
    @fakeshadow 能在语言中实现 a.b(p1, p2) 和这门语言是面向对象的是不同的,实际上真正的函数签名是 b(*a, p1, p2),a.b () 是一个语法糖。比如,rust 中可以用 Astruct::b(*a, p1) 方式调用,说明 b 函数只是 Astruct 命名空间下的一个函数而已。这种语法糖与 java 的类,方法是完全不一样的。
    douglarek
        42
    douglarek  
       2019-04-22 09:41:17 +08:00 via Android
    多说一句,就 py 这种含混不清的方式谈什么优雅,你要是多留意一下 Java 你就知道 Java 中数组才有 length,list 这种是 size,区别就是前者是固定长度,后者是可扩容
    Nicoco
        43
    Nicoco  
       2019-04-22 09:45:12 +08:00   ❤️ 3
    @Livid 公众号小白文引流,严重降低社会内容质量
    102400
        44
    102400  
       2019-04-22 09:49:23 +08:00
    在这方面 Ruby 就要好得多
    baojiweicn2
        45
    baojiweicn2  
       2019-04-22 09:50:24 +08:00
    @vincel def __len__不行吗。你们是不是对 py 不太熟悉啊。len,range 是 python 设计中很好的鸭子方法,更像是其他对象里面的接口。
    walpurgis
        46
    walpurgis  
       2019-04-22 09:51:24 +08:00 via Android
    更喜欢后置写法,因为习惯先写变量名,再给它包上 len,敲起来没有后置方便,要额外调整一次光标位置,而且括号嵌套多了,可读性也不如后置写法链式调用
    XIVN1987
        47
    XIVN1987  
       2019-04-22 09:51:28 +08:00
    只是一种选择而已,,不必瞎吹,,
    mringg
        48
    mringg  
       2019-04-22 09:52:53 +08:00 via iPhone
    其实没有必要做对比,讲讲 len 怎么实现的就好了
    GeruzoniAnsasu
        49
    GeruzoniAnsasu  
       2019-04-22 09:52:53 +08:00
    求分析一下 c++的 std::begin std::get 之类的是什么设计思想
    trait
        50
    trait  
       2019-04-22 09:53:16 +08:00
    @fakeshadow 你对 trait 理解有偏差,看看 rust 里面的方法完整语法再说吧。
    nullcoder
        51
    nullcoder  
       2019-04-22 09:53:23 +08:00   ❤️ 2
    原文
    https://docs.python.org/3.6/faq/design.html#why-does-python-use-methods-for-some-functionality-e-g-list-index-but-functions-for-other-e-g-len-list

    我觉得翻译上似乎丢了些什么

    As Guido said:

    (a) For some operations, prefix notation just reads better than postfix – prefix (and infix!) operations have a long tradition in mathematics which likes notations where the visuals help the mathematician thinking about a problem. Compare the easy with which we rewrite a formula like x*(a+b) into x*a + x*b to the clumsiness of doing the same thing using a raw OO notation.

    (b) When I read code that says len(x) I know that it is asking for the length of something. This tells me two things: the result is an integer, and the argument is some kind of container. To the contrary, when I read x.len(), I have to already know that x is some kind of container implementing an interface or inheriting from a class that has a standard len(). Witness the confusion we occasionally have when a class that is not implementing a mapping has a get() or keys() method, or something that isn ’ t a file has a write() method.

    我对
    Compare the easy with which we rewrite a formula like x*(a+b) into x*a + x*b to the clumsiness of doing the same thing using a raw OO notation.
    的理解是, 纯 OO 的方式写(a+b).len() ,没有 len(a+b)好,因为纯 OO 的方式 (a+b).len() 一般要先 a + b 然后 对结果再 .len()
    而 len(a+b) 可以优化为 a.len() + b.len(),而如果为了避免 a+ b 再求 .len() 写成 a.len() + b.len()上,代码则缺乏优雅
    两个对象不够明显,如果是多个 len(1+2+3+...+n) 和 1.len() + 2.len() + ... + n.len() 就比较明显了。
    rocbomb
        52
    rocbomb  
       2019-04-22 09:55:07 +08:00
    看的我尴尬症犯了

    一个弱类型语言,还瞎搞语法糖,累不累啊
    baojiweicn2
        53
    baojiweicn2  
       2019-04-22 09:55:18 +08:00
    这篇文章太水了,__len__, __str__, __repr__, __getitem__ 明明是 py 中很好的接口设计思路,硬生生被说成是缺陷。。。。。要是觉得 len(object)不舒服,完全可以自己写类的 len 方法。object.len()这样反倒是非常不优雅,而且对使用 py 的数学使用者而言,这种用法也显得非常非常的蠢。
    trait
        54
    trait  
       2019-04-22 09:57:40 +08:00
    @will0404 强行用 rust 写伪面对对象的人要么就是对 oop 有执念,要么就是 rust 了解的还不多。
    myyou
        55
    myyou  
       2019-04-22 09:59:57 +08:00
    @rocbomb Python 是强类型语言
    fakeshadow
        56
    fakeshadow  
       2019-04-22 10:00:56 +08:00
    @trait 理解是否偏差我不知道,我只知道以前看官方文档的时候,trait object 是在 oop 章节介绍的。
    trait
        57
    trait  
       2019-04-22 10:03:11 +08:00
    @fakeshadow 那只是实现 rust 强行实现部分 oop 特性而已,rust 跟函数式才是亲戚
    fakeshadow
        58
    fakeshadow  
       2019-04-22 10:05:20 +08:00
    @trait 我也只是说了可以写面向对象而已,并没说只能这么写。trait object 本来就有 overhead,我自己也基本不用。
    hushuikun
        59
    hushuikun  
       2019-04-22 10:06:55 +08:00
    @rocbomb python 是强类型
    trait
        60
    trait  
       2019-04-22 10:08:44 +08:00
    @fakeshadow 所以你回复他说的那 rust .len()完全跟面对对象没关系啊
    est
        61
    est  
       2019-04-22 10:14:09 +08:00   ❤️ 4
    len 的好处是一句话一把梭啊

    [len(x) for x in some_2d_array]

    map(len, some_2d_array)

    后者用 .length 就麻烦了。
    vduang
        62
    vduang  
       2019-04-22 10:14:25 +08:00 via Android
    @GeruzoniAnsasu begin 之类的是为了支持数组,数组不是类,没法像 vector 一样定义 begin 方法。c++17 的 size 也是一样的原因,都是为使用模板服务的场景服务的。 python 的 str 是为了支持 int 等基本类型,len 也是一样的
    mytry
        63
    mytry  
       2019-04-22 10:18:38 +08:00
    thiscall
    GeruzoniAnsasu
        64
    GeruzoniAnsasu  
       2019-04-22 10:21:25 +08:00
    @vduang 不是对世界本质的捕捉可太可惜了
    ml1344677
        65
    ml1344677  
       2019-04-22 10:28:49 +08:00   ❤️ 3
    I asked this question to core developer Raymond Hettinger in 2013 and the key to his answer was a quote from the Zen of Python: “ practicality beats purity ”. In “ How special methods are used ” on page 8 I described how len(x) runs very fast when x is an instance of a built-in type. No method is called for the built-in objects of CPython: the length is simply read from a field in a C struct. Getting the number of items in a collection is a common operation and must work efficiently for such basic and diverse types as str, list, memoryview etc. In other words, len is not called as a method because it gets special treatment as part of the Python Data Model, just like abs. But thanks to the special method __len__ you can also make len work with your own custom objects. This is fair compromise between the need for efficient built-in objects and the consistency of the language. Also from the Zen of Python: “ Special cases aren ’ t special enough to break the rules.”
    ------《 Fluent Python 》
    zjengjie
        66
    zjengjie  
       2019-04-22 10:33:32 +08:00
    怎么看都只是类似 Java 里的一个静态方法而已,Java 里用静态方法导入后也能实现这种效果,这还上升到世界观了。。。
    est
        67
    est  
       2019-04-22 10:34:04 +08:00 via Android   ❤️ 1
    @rocbomb 大家尴尬症都犯了。。。弱类型的是 js。py 是强类型。。。
    bwangel
        68
    bwangel  
       2019-04-22 10:50:47 +08:00   ❤️ 3
    恕我说句诛心的话,培训班和这种没有实质内容专讲语法的文章,是中国 Python 生态的两大流毒。
    congeec
        69
    congeec  
       2019-04-22 10:53:06 +08:00 via iPhone
    多学两门语言就不会发这文章了
    也就是函数是和面向对象的区别
    guyujiezi
        70
    guyujiezi  
       2019-04-22 10:54:42 +08:00
    不要什么都上升到哲学高度,这种事情往往只是体现了创造者的个人喜好而已
    cxyfreedom
        71
    cxyfreedom  
       2019-04-22 10:59:43 +08:00
    这个就是语法糖的问题,本质就是通过 __len__ 来实现,golang 里面也是用 len 来获取。
    jadeity
        72
    jadeity  
       2019-04-22 11:55:38 +08:00
    哦,茴香豆的茴字有四种写法。
    belin520
        73
    belin520  
       2019-04-22 12:04:43 +08:00 via iPhone
    看了这么多评论,得出一个结论

    这个公众号不值得关注
    cmower
        74
    cmower  
       2019-04-22 12:13:35 +08:00
    哇塞,这么多人评论,看来是好文章了。我要认真读一下。
    will0404
        75
    will0404  
       2019-04-22 12:15:47 +08:00
    @trait rust 能不能写面向对象,和我要不要用是两码事。我没有说我要用,何来‘强行’要用。
    trait
        76
    trait  
       2019-04-22 12:19:31 +08:00 via iPhone
    @will0404 rust 根本没有面对对象一说,所以是强行懂吗
    lancelock
        77
    lancelock  
       2019-04-22 12:22:37 +08:00
    这叫什么前缀表达式?
    YuxiangLuo
        78
    YuxiangLuo  
       2019-04-22 12:28:16 +08:00 via Android
    这不就是 FP 和 OOP 吗,怎么变成了"前缀表达"和"后缀表达",还上升到世界本质了。
    cholerae
        79
    cholerae  
       2019-04-22 12:38:06 +08:00
    python 很多设计就是拍脑袋糊出来的,这语言里有一些不正交和设计不一致的地方,没必要拔这么高
    AngelCriss
        80
    AngelCriss  
       2019-04-22 12:41:11 +08:00 via Android
    有个东西叫 uniform function call syntax,了解一下?比如 rust 里面,xx.func()就是个语法糖
    largecat
        81
    largecat  
       2019-04-22 12:43:06 +08:00 via Android   ❤️ 1
    人家是通过一个浅显的 len 的例子来解释 py 的编程核心思想和基本规则,
    你们一群人盯着 len 说这说那,,,,
    trait
        82
    trait  
       2019-04-22 12:47:28 +08:00
    @AngelCriss 改名了,现在叫 fully qualified syntax
    楼上一群连 rust book 都没看懂的拿 rust 扯什么 oop,真是让人笑掉大牙
    AngelCriss
        83
    AngelCriss  
       2019-04-22 12:52:31 +08:00 via Android
    @trait rust 吹?告辞
    AlisaDestiny
        84
    AlisaDestiny  
       2019-04-22 12:52:40 +08:00
    像你们这种为了推广而做的软文真是业界毒瘤。
    trait
        85
    trait  
       2019-04-22 12:56:03 +08:00 via iPhone
    @AngelCriss 眼睛有问题?
    contmonad
        86
    contmonad  
       2019-04-22 13:48:38 +08:00 via iPhone
    集合类的操作不只有 length 一种,所以从语言设计上来说还是 Lisp 系那种 multiple dispatch 的 length 比较好。可以参考 Clojure 和 Julia 的设计
    tairan2006
        87
    tairan2006  
       2019-04-22 13:51:08 +08:00
    由果导因就不要瞎写…
    EXE
        88
    EXE  
       2019-04-22 14:06:47 +08:00
    语法糖还是语法翔?
    whoami9894
        89
    whoami9894  
       2019-04-22 14:17:57 +08:00 via Android
    len 不就是个 duck typing 吗,跟 Java 一个静态类型的拉到一起比啥……
    仅仅是函数和方法的区别,而且这个函数内部还是调用了方法,确定这叫化繁为简?
    no1xsyzy
        90
    no1xsyzy  
       2019-04-22 14:46:00 +08:00
    其实本来是想要弄成 a.len() 的啊
    但是在 Python 2 的时候一堆东西不是对象不能用 . 的,所以不得不曲线救国做成 len(a)
    结果内置函数和魔法方法变成标准接口了……
    no1xsyzy
        91
    no1xsyzy  
       2019-04-22 14:54:44 +08:00   ❤️ 1
    如上,稍微有点偏差。
    最开始的时候 len 是由 C 函数完成的,实际上只是去读取 ob_size
    那时候也没有 class,一切新类型都是靠 C 扩展的。
    kaneg
        92
    kaneg  
       2019-04-22 16:10:25 +08:00
    因为 Python 是函数式和面向对象编程的混合体,所以同时支持 len(x)这样的内置函数和 x.__len__()这样的对象方法不足为怪,而 Java 则一开始就定位为面向对象的语言,只可能出现对象方法。
    icenine
        93
    icenine  
       2019-04-22 19:24:30 +08:00
    看到 Python 对世界本质的洞察这句,不自觉就想到王自如开箱三星 Galaxy Fold 的样子
    前些年 Python 被搞数据的带火时,估计设计者都是一脸懵逼,更想不到还有人尬吹
    will0404
        94
    will0404  
       2019-04-22 19:38:52 +08:00 via Android
    @trait 你开心就好
    jkhere
        95
    jkhere  
       2019-04-22 20:15:05 +08:00
    果然 V2EX 上都是大牛,一番回复已经很让人思考
    mxalbert1996
        96
    mxalbert1996  
       2019-04-22 21:05:40 +08:00 via Android
    看了这篇文章,我觉得我这辈子学的第一门语言 BASIC 简直就是化繁为简、纯粹却深邃的极致啊!它对世界本质的洞察是多么的深刻!人生苦短,我用 BASIC。
    chinesehuazhou
        97
    chinesehuazhou  
    OP
       2019-04-22 21:19:59 +08:00
    @aijam 哈哈,这个有意思,谢谢分享
    chinesehuazhou
        98
    chinesehuazhou  
    OP
       2019-04-22 21:27:13 +08:00
    @jinliming2 不是抄答案啊,答案就是自己算出来的。而且比 x.len() 方法去算,要快。....感谢 @ml1344677 同学提到《 Fluent Python 》,写的时候没想起去翻翻,失误啊。
    chinesehuazhou
        99
    chinesehuazhou  
    OP
       2019-04-22 21:35:23 +08:00
    @nullcoder 原文上 Guido 说的比较简略,翻译时没有多想。感谢你的补充解释,我也明白了很多。
    chinesehuazhou
        100
    chinesehuazhou  
    OP
       2019-04-22 21:38:58 +08:00
    @est 这也是个不错的例子,感谢!
    1  2  
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2747 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 11:43 · PVG 19:43 · LAX 03:43 · JFK 06:43
    ♥ Do have faith in what you're doing.