瞎扯淡 谈谈我对 Python 的看法

jakit · 2018年04月28日 · 最后由 ratazzi 回复于 2018年04月29日 · 11140 次阅读

原文: https://zhuanlan.zhihu.com/p/36210899

引言

首先,我在 14 年开始接触 Py,当时还在学校学习数据结构和算法,它是我最好的伙伴,因为我当时买了一本 Py 宝典,我就算上课没听,我看书上的 sample 代码就看懂了老师上课说的内容,然后转换成老师要的 C 程序代码,就算课没听,但是我知道怎么抽象数据结构。

但是,我最近特别讨厌他,因为我工作中用到它,因为我工作中每天都在写不等长的 占位符,我且把 tab 的长度的 space 数和 space (1 byte char) 长度的 space 混为一谈,我真不知道是谁发明这么变态的语言。正如有一天我去吃麻辣烫,我点的“咪咪辣”,然后见一妹子跟她闺蜜指着招牌上写的辣度——“变态辣”说:你别要变态辣,我跟你没完,你要是点变态辣你就是变态所以你怎么吃都不觉得辣。

嗯,就这么变态,我也是这么 think (about Python) 的。 あー、すごく変態だ、俺もそう思った Yes, it is so hentai, I think so.

Python 的好处在哪里

吾辈认为,Python 不亚于就是让你不需要学复杂的数据抽象,就算你没有系统设计能力(尤其没接触过编程刚学的大学生),初学者一般教学入门计算机的认知就是输入输出,然后他们作为初学就真的只需要会用几个变量、一些条件语句、一些基础数据结构(数组、字典)来完成任务或应用程序开发。

你要是跟他们谈物件导向编程(OOP),他们是一脸懵逼的,而且,OOP 的思想是“世界的粒度就是对象,然后每个对象都是一个可复用可继承的数据结构,可派生甚至可有后天行为”,这些东西其实新手真的用不到或者对他们来说,他们不去实施稍微有规模的程序,应该说,他们也没有项目可做,尤其在学校的环境里,不可能给他们一个人去搞一个什么项目烧时间旷课来完成。

所以 Python 对他们来说也是很好的,入门者需要的就是简单的工具,过程化逐步深入学习。而不是一下子来个 Java,泛型、类型擦除、反射都糊他们一脸。

我为什么不喜欢它

你为什么要以缩进来作为代码块的基准

自从我历经多门语言,修炼多门技术甚至自己对词法有了解后。至今我在商业项目上终于又拿 Python 来做项目的时候,我反应过来,是谁告诉你拿占位符形成的缩进来作为一个代码块的基准的?

吾辈认为,虽然这个风格的语言,你不推广,自己用可以,但你别跑出来祸害大众让大众跟你一起这样呀。

内嵌问题

if condition:
    if condition:
        if condition:
            if condition:
    // code
        if condition:
            // code
            // code
            ... 很长一段
            ... 很长一段
            ... 很长一段
            ... 很长一段
            ... 很长一段
            ... 很长一段
            ... 很长一段
            ... 很长一段
            if condition:
                ... 很长一段
                ... 很长一段
                ... 很长一段
                ... 很长一段
        if condition:
    // 啊咧?这是哪一个 if 的子块里面呀?

匿名函数问题

我不得不吐槽 Python 不能创建正常的匿名函数,Lambda 只能短写: lambda x: //code

对于这个问题,Guido 说暂时没办法解决,因为缩进已经限制了语法的结构,如果 lambda 这么做,换行看起来怪怪的,而且如果加上的话 Python 解析器语法解析也不方便,他说如果有黑客能做到的话也欢迎建议。 (然而我看他是自己给自己挖了坑,自己跳进去之后发现早知道不设计缩进语法,现在好了,还跟大家说,有哪位黑客能解决告诉他吧)

所谓的 Zen of Python

我彻底无法理解 Python 的“禅”:

首先,我写代码的时候开了 pylint,然后,我有一行代码这样的:

def method(param={}):

这时候 Pylint 给我警告,告诉我 {} 作为代码风格中间需要有个空格,好,我改成这样的:

def method(param={ }):

这时候 Pylint 给我警告,告诉我 { } 作为参数不能有空格

请问,你想让我怎样?反正怎么写里外不是人。

还有一堆我真不想说,比如每个类、方法都要我去写 doc string,否则红点点(警告信息)跟你没完。

而且还有类跟类定义之间换行两行

包空间中方法与方法之间换行两行

噢,这些强迫症,就是 Python 之禅!

我后来换成了 flake8,终于强迫症少了一些,但是我还是没法容忍:

my_data = ["Name 1", "Name 2" ...]
true_names = [ ... ]
if condition:
    if condition:
        if condition:
            my_data = [v for v in my_data if true_names] # 警告

警告行会有提示:代码长度超过 80 charactors,噢呀,我忘了空格占位符缩进也算在 charactors 里面呀,这样的话,如果缩进到 6 层,是不是根本没法写语句了。(示例只是 if,但是有可能是类定义完了里面有方法,方法里面有 while 还有 if 的常见案例,并不算过分)

写作很痛苦

def method(user_name, id_type, address, local_ip, host_name)
# Too long

这样写会太长而告警,换行了一下:

def method(user_name,
    id_type,
    address,
    local_ip,
    host_name)
# Warning

这样还是告警,提示是换行的话,第二个参数要跟第一个参数的首字母对齐,得这么写:

def method(user_name,
           id_type,
           address,
           local_ip,
           host_name)

警告终于消失,但是,我知道我空了几个空格吗?不知道,有时候我真的得祭出 Python 最佳编程实践专用神器—— 游标卡尺 来量一下我需要空几个格,然后下一行别急着继续空格,最好把上面的空格复制粘贴到下面,或者最好是把第三行开始删掉空格回到上一行右边,然后回车,编辑器、IDE 自动换行,然后应该会自动 对齐到上一行开始。

另外,Python 不支持 switch 语句,很多场景没办法,只能多写几个 if,然后,如果具体值在一个集合里面,你可以用 dict 存一些值,然后 if in,但是,如果具体 case 不在一个集合里面,而是别的场合,那么你只能这么写:

if condition:

if condition:

if condition:

Python 不尊重高级开发者

继承没有 protected

C++ 的继承方式多种,是为了能在没有运行时、虚拟机的环境下依然能组合出多元化的对象继承结构(数据复用结构)。

但是 Python 刚好相反,它让你要么什么都暴露,要么什么都隐藏,不要跟我谈 getattrsetattr 之类的元类特性,你觉得我为了 protected 我每次用都做这些好玩吗?

使用已经公认差评的多重继承

多重继承本身就是个 deprecated 的做法,它会有钻石继承的问题,而且 Python 绝大多数书上都让新手去绕开这个坑,而不是语言级别去解决这个问题。就是语言不去解决问题而是把问题交给用户自行解决。

还是刚才说的 lambda 问题

Java 支持匿名类,C++ 既支持匿名函数也支持匿名类。

就连我黑了上万遍的 PHP 都支持匿名函数(closure)和匿名类。

但是,我还是包容的,因为 Python 对象允许后天行为的学习(为实例增加方法和属性)。但是写起来不成体统也不直观。lambda 发明就变成残废,你以为每个人都是数学家吗写个短公式不能换行做递归计算?递归?我记得 Guido 在讨论递归的时候是听厌恶的,他说他的 Python 是杜绝递归,不做尾递归优化,虽然网上有人提出 tail_call_optimized 修饰器的解决方法。

无论怎么说,网友的想法是好的,用户能去这么容忍 Py 不去怎么样而自行解决问题,说明用户都是好心人。

我要开始怼喽

Guido van Rossum 估计是一个患有天才病(阿斯伯格综合征)的病人。

这样的人人际交往困难。患者有与人交往的强烈意愿,却缺乏相应的技巧,所采取的交往方式刻板、生硬、程式化,更无法理解表情、眼色、肢体动作等非语言表达的信息。所以患者往往不受欢迎,也不能灵活地应对场景变化,难以融入群体,建立并维持良好的人际关系。

去反观一下 Zen of Python,去看看 pep8,无处不体现这些特征。

The Zend of Python

  • Python 自以为自己用着很优雅的,自认为优美,实际上在强迫用户去把代码辛苦地浪费时间格式化来显得很标致。
  • Python 自以为自己用着很明了的表达,实际上逼迫着用户有且只能用有限的表达方式和实现来完成任务。
  • Python 自以为的简洁去面对 Java 的复杂 PHP / Perl 的杂乱,实际上实现真正能模拟接近复杂业务的时候 if if if if 都不如 - Java 一个 reflection / annotation 来得紧要,不如比 PHP / Perl 的 $obj->{$requestmethod} 来的及时。
  • Python 自认为的扁平不如 JS Promise / JxJava / Ruby Fiber 也有而且还可以带上长的、真正能发挥作用的匿名函数的非嵌套。
  • Python 自认为的间隔是在按捺代码编写者的耐心。
  • Python 自以为的可读性是牺牲编写容易性,甚至代码编写效率,影响开发者耐心,影响项目质量和进度。
  • 所谓的“规则至高无上”,是建立在里外不是人的变态苛刻编写制度之上。
  • 不要包容所有错误,为何你不像 TypeScript 静态化?做事有头有尾,没必要文人面前扮演武人。
  • 存在多种可能,不是尝试去猜的机会都没有,为了所谓的简化去用所谓唯一一种明显的万金油方案,然而,万金油程序往往是被有经验的开发者忌讳的。
  • Python 之父不是神,是个听不懂人话只会 ABC 的智障(当初就是为了开发 ABC 顺便发明 Python 的)
  • 不假思索就动手还不如不做说明你没听说过热闹驱动开发,不过吾辈认为 Python 就是现在热闹驱动开发炒出来的火,都没验证这门语言是不是 SB 就已经开始火了
  • 无法向人描述你的方案是不是好方案不是谁说了算的,请不要过于自我强调
  • 命名空间是一种绝妙的理念,我们应当多加利用,嗯,是的,但是相信 Python 开发者会把它 import somepkg.vara 来用,尤其 Python 大名鼎鼎的框架 flask,是这么用的:from flask import Flask,但是 app = Flask(name) 之后,app 就形成了耦合,app.db app.logger 等,我的意思是如果外部 from app import app.db 之后,如果其它模块需要引用或者那些模块再反向引用回 app 会出现惊喜(比如 controller 引用 db db 引用 app app 引用 controller),这跟 C extern var 一样的,全局变量、外部变量这种东西,你觉得这是好的编程方式?你就不怕被 OOP style 那群人黑一番?

这就是我在标题用【掩耳盗铃】的原因,我认为 Guido 一直在做这种事情,他一味地崇拜 ABC,把开发者当傻逼,他以为他从语法上去强制洁癖了代码就把代码变得圣洁。

稍微熟悉点的开发者,甚至绕开 Python 这种机制去写出非常难调试的场景,比如利用 setattrgetattr 等来私藏很多“元”,就算外表调用光鲜,内部还不是一样晦涩,而且这样还非常难调。这样的类让人看起来“正直”,但是实际上是个犯罪系数很高的类,然而你从语法的简洁能看出来吗?根本看不出来。

正如很多很多销售都穿得很正装,他是魔鬼就要你去被他套圈子套路。你上当了但是你还认为他是个好好先生,好男人。

从限制语法来限制开发者的单一调用方式万金油式地解决问题就是个坑,你以为你是上帝一行代码解决所有问题,那你这一行代码得为了适应千万场景写多少天?

搞得像服务员小姐,就是抿个嘴都得先拿个东西挡一下,然后再去擦拭,那勺子吃饭要讲究礼仪抬起哪只手,否则会被老板(语法检查器、解释器编译时)批评(警告)甚至是处分(Error)。

工具是拿来用的,不是拿来看的

你 tab space 那么多,给谁看,耽误了工期,你在字里行间的词汇中写出朵花来都没用。

函数逻辑没写对,doc string 写得再详细,那也是欺骗。

就算你 space tab 出了 全宇宙圣经级 pep8 代码,但是跑不起来,设计架构也不完整不成熟,实现效率低出品慢。那又有何用?

不过我倒是悟出了一个真相,一个道理:

Python 是写来装逼的(外观式编程)

相信 Python 在礼仪程序(Python 写的不是用来运行而是拿来作为礼物送人给别人看)讨好上司看自己写的代码多么礼仪还是很有价值的。

第一个观点:

然而我并不需要这种写了半天都没格式化好还有浪费时间在因为不提倡 if while 嵌套不能多行匿名函数我得绕各种弯子解决问题,浪费时间在代码风格上的语言。

第二个观点:

我需要的是,我写得舒服,看着清晰、正确,作为 adult 就应该自觉去格式化代码而不是被代码格式化暴力与强奸,应该是我写代码不是代码写我。

有时候为了写出符合 pep8 风格又要达到算法优化级又要完成任务,那简直是多烧脑,不如去写 Haskell Ocaml,但是王垠在《我为什么不再做 PL 人》是这么评价的:

第一段

有一些 PL 人喜欢推广他们认为高大上的语言,比如 Haskell,OCaml,Scala 等。这些语言在 PL 学术界很受尊重,所以他们以为这些语言能够奇迹般的解决实际的问题,然而事实却不是这样的。事实是,这些学术界出来的语言,其实缺乏处理现实问题的机制。为了能够在学术上证明程序的所谓“正确性”,而且由于类型系统本身的局限性,这些语言往往被设计得过于简单,具有过度的约束性,以至于表达能力欠缺。

最后,你发现用这些语言来写代码,总是这也不能做,那也不能做,因为你要是那么做了,编译器就无法发现“类型错误”。到最后你发现,这些语言的约束,其实是无需有的。如果放宽这些约束,其实可以更优雅,更简单的对问题进行建模。对正确性的过分关注,其实导致了 PL 人选择蹩脚的语言,写出绕着弯子,难以理解的代码。

第二段

这种天才病的危害在于,它蒙蔽了这些人的眼睛。他们不再能设计出让“普通人”可以容易使用的产品。如果你不会用,他们就会嘲笑你笨,而其实呢,是因为他们的设计不好。他们喜欢用含混晦涩的方式(所谓“函数式”)的写法来构造代码,让其它人阅读和修改都极其困难,……


关于 lambda 的讨论,我想引出来的是一门语言,要么提供匿名函数,要么提供匿名类:

C++

std::function func = [](type param) {
    // code
}

用过 cocos2d-x 的人都知道绑定 event 最常用的方法就是它

JavaScript

function MyIterable { this.arr = [] }
MyIterable.prototype = Object.create(Object.prototype);
MyIterable.prototype.constructor = MyIterable;
MyIterable.prototype.iterWith(func) {
    this.arr.forEach(function(item) {
        func(item);
    }
}

当然,这些东西放到抽象类作为等到子类去实现也是可以的:

PHP

abstract class MyIterable {
    $arr = [];
    public function iterWith() {
        foreach($this->arr as $value) {
            $this->iterWithImpl($this->arr);
        }
    }
    abstract public function iterWithImpl($item);
}

$my_iter = new MyIterable {
    public function iterWithImpl($item)
    {
        echo $item;
    }
}

当然 PHP 也可以用 closure 匿名函数解决:

class MyIterable {
    $arr = [];
    public function iterWith($callback) {
        foreach($this->arr as $value) {
            $callback($value);
        }
    }
}

$my_iter = new MyIterable {
    public function iterWithImpl($item)
    {
        echo $item;
    }
}

以上说明 PHP 提供两种方案。

Java 的例子,跟 PHP 基本差不多,而且 Java 还可以用 Lambda 来便捷地创建匿名类(以下摘自安卓 button 监听):

public void myBtnClick() {
       btn.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
               Toast.makeText(getApplicationContext(),"这是弹窗",Toast.LENGTH_SHORT).show();
           }
       });
}

What about Python?

Python 既没有匿名类,lambda 也是个残废

这样的语言,你说写起来能不绕?写起来绕就算了,但是宗教教条一样的语法限定又怎么说,那就完成一件事不仅仅要注意它的教条规范,还得去绕着弯来做事,这是多么的困难。

Python 教条主义符合以下特征:

  • Python 为了能够在证明他的程序代码能保证的所谓“Zen 的简洁性”
  • 这些语句往往被设计得过于简单,具有过度的约束性,以至于表达能力欠缺
  • 你发现用这些语言来写代码,总是这也不能做,那也不能做,因为你要是那么做了,pep8 warning 和 Python 解释器跟你没完
  • 到最后你发现,这些语言的约束,其实是无需有的

@rocLv 的回复跟让我可笑,同时也正好符合

  • 如果你不会用,他们就会嘲笑你笨,而其实呢,是因为他们的设计不好。他们喜欢用含混晦涩的方式(所谓“函数式”)的写法来构造代码,让其它人阅读和修改都极其困难
  • 他们不再能设计出让“普通人”可以容易使用的产品
Rei 回复

我说这是瞎扯淡的,所以归类到瞎扯淡,反正不喜欢这种强迫癌晚期的工具。

尝试一下被强迫症施压的感觉是多么的扭曲和变态简直无法忍受

不过这句话倒是有价值的:

每个存在的语言都有可取之处,工作不得不用却不去发现它的优点那损失的是你自己

if a:
    if b:
        if c:

为什么不写成

if a and b and c:

flake8 可以配置每行最长多少字符,或者可以忽略

form is liberating,语言只是工具

有些偏激,但是从使用角度来说,Python 确实很糟糕啊,没毛病。。

duyue 回复

不是所有场景都能一次性 and and and 的,如果 result_1 在 第一个 and,result_2 在第二个 and……不一定说全都是 and,如果是:

while condition
    if condition
        if condition

等等组合呢,不一定非得是

我说得有点激动,但是事实上我项目中实际这么一用,五味杂陈,啥感觉都出来了。

例如有人单纯因为不喜欢 end,不喜欢 @ 就把 Ruby 整个否定的。这样就错过了 Ruby 的 DSL,元编程,Rails。

Rei 的评论我是 OK 认可的,但是我只是觉得 Ruby 至少没那么强迫症,你可以不这么写嘛,Ruby 的文化就是多元的,不止一种方式做事。你不喜欢 Ruby 这种,你可以喜欢 Ruby 另一种。

但是 Python 就不可以,你有且必须这么强迫症,像是把你捆起来 SM 一样的。

但是就你举的例子,是可以 and 的;你最好举一个能支持你的言论的例子。

while condition
    if condition
        if condition

可以写成

while condition
    if condition and condition

既然你说 ruby 不存在这种问题,那请问在 ruby 里面你会怎么写?

if condition:
    if condition:
        if condition:
            if condition:

代码写成这样,换什么语言都没得救

huacnlee 回复

等你用来写了之后,你就会发现很多场景这么写不是故意的,因为除了这么做,没什么别的好用的语法了。

就算你把逻辑用一个个函数分开来,你会发现,到处都是没必要定义函数。

duyue 回复

实在没办法,只好举例子:

until condition
end
while condition do
  case n:
  when a:
    break
  else:
    # code
  end
end

我说了,不是所有场景都能一次性 and and and 的,如果 连续 几个 and 都是嵌套:

data = {
  nest_data = {
    nest_nest_array = [
      { "nest_nest_info": "info" }
    ...]
  }
}

解析这种场景如何?当 key 不规则

我知道可以有算法处理,但是有时候是赶时间完成任务的,不是去为了绕几个弯设计算法的。

然后还有如果先筛选判断输入,后处理输入后写入存储后判断结果还有处理,3 - 4 个 缩进不算过份吧,不一定说【都是】这个词,我的意思就不都是 if 或者都是 while 什么的。

对于数据 filter / reduce,Python lambda 只有一行,有很多场合处理起来真的不容易,你要么另外写一个函数,但是这样如果只针对某个数据结构场景,那是不是每个数据结构、业务场景都写个函数?

但是 Ruby 可以:

hash.select do |key, value|
   if key == 'name'
     # code
   end
   # code
end

而且 Ruby 有强大的 Enumerable 而且跟灵活的 Proc 结合在一起,非常灵巧。

具体业务具体场景具体分析,反正 Python 就是写着很棘手,语法很干很硬,便秘一样的。

有时候为了写出符合 pep8 风格又要达到算法优化级又要完成任务,那简直是多烧脑,不如去写 Haskell Ocaml,但是王垠在《我为什么不再做 PL 人》是这么评价的:

第一段

有一些 PL 人喜欢推广他们认为高大上的语言,比如 Haskell,OCaml,Scala 等。这些语言在 PL 学术界很受尊重,所以他们以为这些语言能够奇迹般的解决实际的问题,然而事实却不是这样的。事实是,这些学术界出来的语言,其实缺乏处理现实问题的机制。为了能够在学术上证明程序的所谓“正确性”,而且由于类型系统本身的局限性,这些语言往往被设计得过于简单,具有过度的约束性,以至于表达能力欠缺。最后,你发现用这些语言来写代码,总是这也不能做,那也不能做,因为你要是那么做了,编译器就无法发现“类型错误”。到最后你发现,这些语言的约束,其实是无需有的。如果放宽这些约束,其实可以更优雅,更简单的对问题进行建模。对正确性的过分关注,其实导致了 PL 人选择蹩脚的语言,写出绕着弯子,难以理解的代码。

第二段

这种天才病的危害在于,它蒙蔽了这些人的眼睛。他们不再能设计出让“普通人”可以容易使用的产品。如果你不会用,他们就会嘲笑你笨,而其实呢,是因为他们的设计不好。他们喜欢用含混晦涩的方式(所谓“函数式”)的写法来构造代码,让其它人阅读和修改都极其困难,……

用 Ruby 虽然我也可以拿 block 很快解决问题,虽然匿名函数块没那么好维护,但是至少能先解决问题。

Python 与 pep8 强制性过份地强调那种他自认为很神圣很学术总之接近于 法 lun 功 迷信信仰一样的语法风格信仰,固化了语法去用 4 space 就算了,还要让大家字里行间写得非得他认为的整齐整洁,我觉得那才是问题。

另外,吾辈认为 Python 推崇的 简洁 不代表 简单 ,你想想看你去打扫一个房间,其实你只要把屋子收拾整齐,不留太多毛病就够了,最好就是能清洁一下然后让人看着舒服,但是那些都算优化。

但是 Python 就像是打扫一个房间就搞得像是医院强制消毒一样的,投放各种消毒剂、清洁剂去把各种东西非得严格处理一样的。

爱干净洗手很好,但是你在别人面前先拿肥皂洗手,再拿消毒液洗,再拿护理液洗,你旁边的人看你这样洗你觉得你自己很 tidy 但是我看着不以为然,反而觉得有病。

楼主喷的蛮有道理,实际出发最好,讨厌教条。

jakit 回复

你这 Ruby 写得也丑啊。。

while condition do
  case n:
  when a:
    break
  else:
    # code
  end
end

完全可以用 guard clause 风格消掉嵌套,任何语言都可以这么搞

while cond do
  break if a
  break if b
  # your code
end

我大概看了一下,作者喷的几点:

  1. 格式问题
  2. 多重继承
  3. lambda
  4. protected
  5. 条件选择嵌套

第一个问题是和语言无关的,而且如果写也可以写成def foo(*param): 第二个问题 python 是 1991 年发明的语言,多重继承在哪个年代确实解决了当时的问题,多重继承的弊端也是工程实践后发现的。离开时代,评价语言是不是有点。。。。 第三个问题每一个设计都有应用场景,都是满足某一个需求而设计。为了喷而喷没意义。 第四个问题参见Protected method in python 第五个问题,如果是我我都没脸写出来。作为多年的程序员,用一个 OOP 语言,写出这样的代码,可以说你对 OO 编程缺乏理解,是不是应该去补补面向对象编程、重构、设计模式等方面的知识了?

jasl 回复

不过有时候这样写容易改变本来逻辑的形式使得逻辑和实现之间的对应不直观。

就是本来我可以简单描述的事物由于语言的限制我只能绕着说,这样就导致了不方便。

jasl 回复

我知道你会这么写,所以然后正好符合我喷的 Python 逻辑:

while cond:
    if conditionA:
        # code
        break
    if conditionB:
        # code
        break
    if conditionC:
        # code
        break
    if conditionD:
        # code
        break

if 是正向的,然后 Python 没有 until,所以你得 while 了再 if

在代码简洁之道里面有讲,我知道有人会用防御式去减少内嵌,原文没说不代表我不知道,我只是陈述某中情况,它确确实实会发生你不说一定绝对能用防御式内嵌能解决的

rocLv 回复

你说的一切正中我下怀

Python does not support access protection as C++/Java/C# does. Everything is public. The motto is, "We're all adults here." Document your classes, and insist that your collaborators read and follow the documentation.

尤其 We're all adults here.

这句话就是我非常要喷的,好,你 Python 这么 adult,干嘛要让人这么填鸭一样幼儿园在日字格去写数字 1,斜着,2 要笔顺从怎样多少度弯曲然后底部线要贴日字格底地去用?

相反,Python is an ABC language! Guido design his Python as an ABC language!

所以说,如果你认可容忍这样,而且你还觉得

可以说你对 OO 编程缺乏理解,是不是应该去补补面向对象编程、重构、设计模式等方面的知识了?

我觉得你应该得把所有编程语言都得重新学一遍,重新看看人家 Perl 怎么用 Moose OOP,哪怕不 OO,人家就算没有这样的环境都能自觉地 adult 形成这种思维。

设计模式不是对所有语言都适用,不是所有语言都适用你所谓的那本《设计模式》里面的模式,Ruby 就有 mixin 式代理,为什么一定要定义一个空的方法去作为接口?直接调用一个不存在的方法不就完了,PHP 还可以 $obj->{'method_name'} 去 invoke 方法,按照设计模式去做,是不是得 Method m = class.getMethods ....再去 m.invoke(obj, params ...)

我觉得你对任何语言都不熟悉,从计算机基础数据结构都得重新学!你连 OO 本质的抽象模型与数据复用想法估计都是错的!

估计你没写过什么语言,没什么使用经验

看了你的 Github,果然如此,你作为多年程序员,你也就是写写 Web 写写网站吧,难怪得出这样的结论

你的 Stackoverflow 帖子提到的语言你得去补一下并且去任何一个公司做这些语言起码每个 2 年再回我帖子

jakit 回复

until 那不就是 while !cond 的语法糖么?

unless 也就是 if !cond

while cond:
    if conditionA:
        # code
        break
    if conditionB:
        # code
        break
    if conditionC:
        # code
        break
    if conditionD:
        # code
        break

你这代码有什么问题?Guard clause 作为一种标准编码风格不来了解一下?

lithium4010 回复

确实不直观,只有少量现代语言才提供了 Guard 语义,但这样写也是业内的一种最佳实践,这种编码风格有专门的术语叫做 guard clause,参考资料见我上一个回复

jasl 回复

我没说防御式编程有问题呀

认真看我回你的原文:

我知道有人会用防御式去减少内嵌,原文没说不代表我不知道,我只是陈述某中情况,它确确实实会发生你不说一定绝对能用防御式内嵌能解决的

你不用列帖子给我,我手头的《代码简洁之道》比你这些帖子对 guard clause 陈述的更完整

jakit 回复

可是你给的例子还是防御式编程可以解决的啊,怎么解决不了了呢

把 condition 里超过 3 行的 code 抽成函数就行了。

这样主流程里全是 condition,就清爽了。

你觉得“没必要定义的函数”,恰恰或许是 dirty 的原因。

jasl 回复

我跟我同事说:防御式编程让我感觉太难受了

祭出文章《编程过度防御?只有缺乏自信的程序员才会这样做》

https://blog.csdn.net/csdnnews/article/details/79597639

祭出书本《优雅的 Ruby》,里面有一章关于一开始就去这样防御让人看起来很恶心的讨论。

另外,Python 很多时候写着防御式,有些我做的是 type casting,就是 a is b,写着写着,感觉还不如去用 Java C++,静态语言类型可以编译器检查,何必我这样手写着累。

这就是我文中说的

为何你不像 TypeScript 静态化?做事有头有尾,没必要文人面前扮演武人。

nine 回复

是的,抽成函数,所以这就回到了我上面说的:这样的话没必要的命名函数就很多了。

真正的项目场合,你就是一大堆不打算公开的函数,怎么说呢,写起来就像是大学生刚学 C 语言,把功能拆散成一个文件里面 N 多个函数,不归类,如果时间赶也可能来不及整理。

jakit 回复

如果你可以保证程序准确表达你设想的逻辑并且能够让合作伙伴相对容易的理解,那么什么编程风格无所谓。

  • 首先 Guard Clause 就是用于明确消除嵌条判断的,作为 Pattern 这是业内共识
  • 其次防御式编程跟特定语言关系不大,这段代码你用 Py 用 Ruby 用几乎任何语言,写出来都是差不多的,这并不是 Py 的问题,Ruby 也没好哪去
jakit 回复

还有,我建议你去那文章的原文,首先你发的 CSDN 翻译版本就是标题党,改变了原文标题的语义

原文标题 Overly defensive programming 意思很简单 过度防御式编程

其次,原文里没有否定防御式编程,而是在说有一些做法属于过度,没有必要

jasl 回复

你把整个项目写完,然后几乎每个 input 都防御一下,跟 Overly 有什么区别

先不讨论是否 Overly,再加上 Python 没有 switch 语句,你得写更多的 if

jakit 回复

那显然就是那文章要讨论的了

作者并没有否定防御式编程,而是在说有一些做法属于过度,没有必要

作者前几段指出了一些典型的过度防御的场景,最后给出了建议

When writing code, take a minute to think through the edge cases.

  • What kinds of errors might happen? What would cause them?
  • Are you handling the errors you can foresee?
  • Could the error occur in production, or should it be caught during development?
  • If you provide a default value, can it be used correctly downstream?

我觉得把这些翻译过来,他的意思很明确了

jakit 回复

而过度防御,并不是语言的问题,而是人的问题,显然语言本身并不约束防御过度这件事

jasl 回复

好,那你怎么解决 switch

jakit 回复

我不知道你在说什么,你上文给出的所有例子在我看来(包括我重新看了眼资料)用 guard clause 风格都没有问题。

switch 有什么问题么?需要做匹配正常用就是

jasl 回复

嗯,你去看看 Py 有没有 switch 语句

jakit 回复

但是 Py 有 if ... elif ... else 和 switch 是等价的,你说难看,可以这么说,但这不是阻碍你编程的障碍

jasl 回复

语法不一样的东西,不算等价吧。

你总不能:

result = [] # 假设 Array
case result
when Array
when 'test'
when 123
else
end

这样的事情,你得:

if isinstance(result, list) and ....
    code
if isinstance(result, list) and ....
    code
if isinstance(result, list) and ....
    code
if isinstance(result, list) and ....
    code
if isinstance(result, list) and ....
    code

或者:

if isinstance(result, list)
    if result == ...
      // code
if isinstance(result, str)
    if result == ...
      // code
if isinstance(result, int)
    if result == ...
      // code

如果多个嵌套呢:

if isinstance(result, list)
    if result == ...
      // code
if isinstance(result, str)
    if result == ...
      // code
if isinstance(result, int)
    if result == ...
      // code

如果解析这样数据结构呢:

result = {
    "name": "name"
    "aaa": {
        [{
            "address": {
                "bbb": 
                  .....省略不想写啦

解析起来这样的:

if isinstance(result, dict)
    if 'name' in result and result['name'] == ...
       if ...
           if
               if 以此类推
           // 啊咧,这是上一层吧?
     // 啊咧,这是上上层吗?(不是,这是上上上层)

你说难看,可以这么说

嗯,是的,这不仅难看,而且我都不记得 space 了几个,尤其我用了 tab_to_space,得很小心

我知道 IDE 很智能,但是 IDE 也有抽风的时候,如果帮你多删了几个 space 的时候,你重新找位置是不是得感谢 IDE 全家呢?

所以我才在文章表露我的意思,这说明 游标卡尺 在 Python 开发中是刚需品,必须得用到的

每一位开发者得买一把 游标卡尺 放在抽屉避免异常情况,你一定用得上的!

jakit 回复

我们不要管 Ruby 先,你这段代码在 PHP 和 Java 里怎么写呢?是否可以用 switch 解决?

jakit 回复

其次,你写的这段代码,在 Ruby 下是什么样子的?

最后,在超越语言的思路上,有没有什么好的解法?

jasl 回复

可以啊

PHP

$arr = ...
switch($arr) {
    case 'test':
       // code
    case 123:
        // code
    default:
        if (is_array($arr)) { // code }
}

其实 Python 有办法解决,但是太恶心了

网上给出了 filter / map 的方式,其实可以用 in 语法,但是那样 很绕

jakit 回复

那么更复杂的模式匹配呢?

jakit 回复

比如正则,是否在某个范围里,是否是某个类的子类 等等情况

无论怎么复杂,反正至少写起来顺手

@lithium4010 说的:

就是本来我可以简单描述的事物由于语言的限制我只能绕着说,这样就导致了不方便。

jasl 回复

https://stackoverflow.com/questions/4043741/regexp-in-switch-statement

Python 应该跟 PHP / Perl / Ruby / JavaScript 比,Java 不要拿进来说,Java 作为静态语言,天生编译时可以类型、行为直接检查完了,很多静态的优势,但是你要拿静态语言去跟脚本比灵活性没意义,所以 Python 应该跟 PHP / Perl / Ruby / JavaScript 比,但是比起 PHP / Perl / Ruby / JavaScript 没有任何优势,除了库比较大

但是只要 Rubygems / npm 发展起来,我并不认为 Py 再有任何优势

我觉得 Py 应该去接近 Ocaml / Haskell 去,因为大家没发现“普通人”用的语言都是 { },begin end,就 Python 拿 tab / space,就 Ocaml / Haskell 用各种函数式串式编程。总之就是 异类

所以说 PHP 的 写出来和 Py 的 if elsif 的风格几乎一样了?

那么你是为了用 switch case 而用么?如果不能用则代表不灵活?

jasl 回复

绕来绕去讨论好费劲……不想聊了,不回复了

在最后最后,我举个极端的例子

假如,你的 IDE 抽风了,把你的代码:

if isinstance(result, list)
    if result == ...
      // code
if isinstance(result, str)
    if result == ...
      if isinstance(result, int)
         if isinstance(result, int)
            if result == ...
                // code
         if isinstance(result, int)
             if result == ...
                 // code
if isinstance(result, int)
    if result == ...
      // code
    ... 省略

这样结构的,突然 IDE 一抽风,或者你不小心按 tab 加入了 shift 键盘,那么成了这样子:

if isinstance(result, list)
if result == ...
  // code
if isinstance(result, str)
if result == ...
  if isinstance(result, int)
     if isinstance(result, int)
        if result == ...
            // code
     if isinstance(result, int)
         if result == ...
             // code
if isinstance(result, int)
if result == ...
  // code

或者这样子:

if isinstance(result, list)
if result == ...
// code
if isinstance(result, str)
    if result == ...
if isinstance(result, int)
if isinstance(result, int)
    if result == ...
        // code
if isinstance(result, int)
     if result == ...
         // code
if isinstance(result, int)
        if result == ...
// code

或者这样子:

if isinstance(result, list)
if result == ...
// code
if isinstance(result, str)
if result == ...
if isinstance(result, int)
if isinstance(result, int)
if result == ...
// code
if isinstance(result, int)
if result == ...
// code
if isinstance(result, int)
if result == ...
// code

我这里是给了 // code 所以你看得出来,但是现实逻辑是残酷的……如果 IDE 还在抽风,你在撤销的时候并没有还原完整成了:

if isinstance(result, list)
if result == ...
// code
if isinstance(result, str)
    if result == ...
if isinstance(result, int)
    if isinstance(result, int)
if result == ...
        // code
    if isinstance(result, int)
    if result == ...
         // code
if isinstance(result, int)
        if result == ...
// code

if 的先后顺序,逻辑都彻底不对了!

当然大的变动可能性不大,小的有可能,比如你在 if 前面的空格不小心 backspace 错了,但是没察觉没在意,继续写代码,过后你发现逻辑出问题了

jakit 回复

就你举的从嵌套数据结构提取数据的例子,这些写法都不好

而且 这个场景 用 if elsif 还是 case when 都不是问题的关键

你应该用 jsonpath 解决

jasl 回复

你去看看是不是所有人都拿 jsonpath 来做这些的?你的前端给你传个 fomdata 你也去 jsonpath parsing 吗?

另外,我们又不是审计局,我不做精算师,不是数据分析员

做个小后端虽然只是增删改查,要不都把 matlab / R 也调进来 parse 一下某个 key value 吧,逼格更高一些

要不我们别写代码了,让 AI 自动分析我们的代码 tab 缩进几个了然后自动化为业务 generate 代码吧

jakit 回复

如何去理解问题,精妙的解决它,不就是评价编程能力高低的标准么?

jasl 回复

听到你的回复我已经安心了

47 楼 已删除
if all([cond1, cond2, cond3, cond4]):
     pass

这种写法会好看些,但只针对某些情况

jakit 关闭了讨论。 04月30日 01:18
需要 登录 后方可回复, 如果你还没有账号请 注册新账号