4、需要反复使用一组参数的时候,传递一个指针(或者C++的引用)比重新在栈里复制一次开销小一些

5、二进制连接,而且不支持重载,希望通过结构体头部字段(比如Windows下通常有个字段表示结构体大小)来支持扩展

Java当中也经常使用这种接口形式,理由跟上面其实有类似的地方:

1、参数太多,完整的参数列表太长,尤其是要把类型全都写上的时候(很多泛型类2、型名字很长)

3、没有特别有效的方式表达复杂的参数格式,虽然支持List、Array、Map等基础数据类型,但构造包含数据的这些类型很困难

4、可能的性能损耗

5、不支持默认参数

6、希望能通过传递接口的方式支持多态

这些理由当中第二条和第四条直接跟Java语言的缺陷有关,第五条看上去很OOP,实际上我认为完全是个设计错误。我们回到一个比较本质的问题:

怎样的参数列表是清晰的?

我们想一下,当我们拿到一个参数列表的时候,什么情况下我们可以在最短时间内知道如何调用它?

1、所有的参数都是你熟悉的类型

2、所有的参数是单一职责的,你很清楚传入的这个参数起什么作用

3、所有的参数不会在传入之后产生副作用

如果传入的参数是个对象呢?

1、我们很可能不熟悉这个对象的类型。尤其对于动态类型语言来说,这个参数可能2、没有明确的类型

3、我们无法知道这个对象是用来干什么的,究竟其中的哪些属性被用到了,哪些方法会被调用,如果我们希望在动态类型语言中造一个duck type的对象,应当最少提供哪些成员

4、我们不知道这个函数是否会修改这个对象,是否会在内部保存这个对象,这个对象的所有权究竟是移交了还是仍然保留在我手里还是由我跟其他人共享,我是否能够再次复用这个对象等等

尤其是如果你希望通过传递一个接口类型来实现多态,我们会遇到怎样的困境呢?

1、当我需要添加新的功能的时候,我很可能需要修改这个接口。这意味着所有调用这个函数的地方都要做相应的修改,要为它们的实现添加这个新的接口。

2、接口中用到的各个方法和属性是紧密耦合在一起的。即使是最严格的实现规范,也很难保证这些实现不同的接口在各种调用方式下都保持一致的特性。某个版本可能会调换了接口中两个方法的调用顺序,仅仅是这样就可能会带来新的bug。

对Python来说

我们回到之前Java选择传递对象作为参数的理由,我们对比一下Python

1、动态类型语言不需要写类型名, 所以参数很多的情况下看起来也不会特别长

2、非常容易构造复杂的结构化数据,比如元组、字典、列表等

3、性能。性能??

4、不仅支持默认参数,而且支持使用keyword-argument来传递参数,因此可以在很长的参数列表中,有选择地传递少数几个,还支持*args,**kwargs这样的可扩展参数,没有必要依赖对象来实现扩展性

5、可以传递函数、boundmethod等callable作为参数

前面应该都比较好理解,我们着重说一下最后一点。我们有的时候需要调用方为我们提供一些用于回调的方法,Java当中通常需要传递一个接口。如果说需要传递的回调方法不止一个,就可能会合并在同一个接口中。

而Python直接就可以传递一个callable,这带来了很多的好处:

1、单一职责。你很清楚传进来的这个函数是需要被调用的,以及如何被调用;它不需要跟其他传入的参数有联系。这也是最重要的好处。

2、灵活性。Python的callable可以是一个普通函数,可以是一个闭包,也可以是实例的boundmethod,还可以是一个lambda表达式,还可以是一个类,还可以是一个实现了__call__的类的实例,调用方可以很容易进行自由选择,自由实现

3、多个callable完全不需要有联系,可以分开实现,分开复用

4、可以有缺省值,这样调用方如果只需要默认的行为,可以不传

Python函数参数设计的准则

基于以上理由,我个人总结的函数参数设计的准则是这样的:

一:参数必须是某个基础类型。

这包括内置类型如字符串、整数、元组、列表、字典等,也包括一组贯穿你整个项目的基础的类的实例,这些实例不管是开发你项目的哪一个部分都是必须知晓的,比如说异步编程的接口需要传入一个loop对象(或者调度器对象),这没有什么问题,如果调用方不知道什么是调度器那他大概也不用往下写了。不应该使用某个专门为了这个功能而设计的类的实例来作为参数。

某种callable也算是基础类型,可以在文档中说清这个callable的参数列表以及类型,这个callable的参数也应当符合这里讨论的准则。这个callable应该接受所有它可能需要的参数,而不是默认它在内部实现机制上知道一起调用时传入的其他参数。如果有特殊的要求也应该声明。

二:对于需要比较复杂的数据的情况,可以使用嵌套的元组、字典等进行传递

在Python中,构造一个对象的代价远远比构造一个嵌套的元组(列表)或字典要高,尤其是列表、字典可以通过生成器表达式来构造的情况下。而且这些数据类型永远可以通过deepcopy复制,不会担心有副作用。

除非必要,sequence类型(包括元组、列表、set、迭代器乃至所有可以迭代的对象)都应当支持,视作同一种类型,可以在传入的时候通过list()或者tuple()转换成确定的类型。这是因为你比较难在文档中描述清楚你需要的究竟是一个列表套列表还是元组套元组。

注意只有当有必要这么做的时候才用这种方法,正常情况下应当尽量使用参数列表。

三:永远不要修改或保存传入的数据

某些内置类型也是引用类型,如列表、字典、set等,永远不要默认调用方将所有权转移给了你,如果你需要修改或者永久保存这个数据,一定要使用list、dict等构造函数或者copy/deepcopy来复制一个副本。

四:重要的参数在前,次要的参数在后且有默认值

这个一般人都应该能理解,虽然可以用keyword-argument,但毕竟比较长,参数很多的时候,大家都会默认后面的参数不看也没什么大不了的。

参数列表设计带来的解耦

这样设计并不是说大家不要用OOP,不要设计类了,而是相反。这种设计是让大家尽量不要把一个类的实例当作另一个类的参数来使用,尤其当它们属于不同的模块的时候。这就带来了模块之间的解耦,从:

类 <=> 类

变成了

类 <=> 最小数据集合 <=> 类

类 <=> 类

变成了

类 <=> 最小数据集合 <=> 类

的关系。这个最小的数据集合也可以理解为一种protocol,它很清晰地描述了我们在这个问题中,需要传递哪些信息。这个集合很容易扩展,在Python当中,只要在参数列表末位添加新的带有缺省值的参数就可以了。

这样的设计还可以很容易直接将函数定义映射到WebService上,我们可以发现如果没有复杂的callable之类的参数,这些参数通常都可以用JSON来表示,那么我们就可以直接将接口映射到WebService上而不需要过多的修改,也许添加一个注解就足够了。

●本文编号204,以后想阅读这篇文章直接输入204即可。

python 多态 知乎_Python函数接口的一些设计心得相关推荐

  1. python 多态 知乎_Python鸭子类型和多态

    鸭子类型 维基百科解释: 当看到一只鸟走起来像鸭子.游泳起来像鸭子.叫起来也像鸭子,那么这只鸟就可以被称为鸭子. duck typing:在程序设计中是动态类型的一种风格.在这种风格中,一个对象有效的 ...

  2. python多态的概念_Python 多态

    Python 多态 一.多态 概念 允许将父对象设置成和一个或多个的他的子对象相等技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特征以不同的方式运作.简单来说:就是允许将子类型的指针赋值给父类 ...

  3. python测试函数怎么写_Python - 函数

    函数,function,是一段代码的集合体.是Python为了代码最大程度的重用.减小代码冗余而提供的最基本的程序结构. Python是一门面向对象的编程语言,对函数进行分类和封装,由对象实现对方法的 ...

  4. range函数python三个参数_python函数--range()方法

    range()方法 range()是python内置函数它能返回一系列连续增加的整数,它的工作方式类似于分片,可以生成一个列表对象. range函数大多数时常出现在for循环中,在for循环中可做为索 ...

  5. python 做网站 知乎_python做网站 知乎的搜索结果-阿里云开发者社区

    伤不起的全栈程序员 前段时间听说一个新名词:"全栈程序员",google了一下,被引导到了知乎的一个讨论上: http://www.zhihu.com/question/22420 ...

  6. python数据接口设计_python之接口与归一化设计

    1接口 接口的概念: Java 语言中的接口很好的展现了接口的含义: IAnimal.java /* * Java的Interface很好的体现了我们前面分析的接口的特征: * 1)是一组功能的集合, ...

  7. python 倒叙 数组_Python函数合集:68个内置函数请收好!

    内置函数就是python给你提供的, 拿来直接用的函数,比如print.,input等.截止到python版本3.6.2 python一共提供了68个内置函数. #68个内置函数 # abs() di ...

  8. python修饰符作用_python函数修饰符@的使用

    python函数修饰符@的作用是为现有函数增加额外的功能,常用于插入日志.性能测试.事务处理等等. 创建函数修饰符的规则: (1)修饰符是一个函数 (2)修饰符取被修饰函数为参数 (3)修饰符返回一个 ...

  9. python爬虫 知乎_python爬虫——知乎(关于python的精华回答)

    之前的文章都是与职位.热门文章有关的,今天一起来看一下知乎上与python相关的精华回答(主要是requests,scrapy处理的思路,编码问题) 知乎python精华回答 1.采集的信息 问题题目 ...

最新文章

  1. GPU对决TPU,英伟达能否守住领先地位?
  2. MarkDown编辑器基础使用教程
  3. python3 __repr__ __str__ 区别
  4. FTP 1 协议分析
  5. 【BZOJ28431180】极地旅行社,LCT练习
  6. 3.8 Anchor Boxes
  7. selenium 定位方式5
  8. 腾讯云坚持“云+数据库”转型,看好多模数据库未来发展
  9. Android开发性能优化大总结
  10. 对select into表复制的一点思考
  11. 安全提示:勒索病毒漏洞与CPU漏洞务必小心
  12. OA系统新流程创建与管理办法
  13. 干净的国内系统镜像源
  14. 如何找回bilibili(b站)收藏夹里失效的视频?
  15. IE8浏览器为什么没有工具-internet选项
  16. Pikachu漏洞练习平台----验证码绕过(on server) 的深层次理解
  17. 梦在远方,而你在心上
  18. CCF CSP 201803-4 棋局评估
  19. 01-邂逅React开发
  20. DevOps 工程师面试问题(持续更新)

热门文章

  1. log4j2_使用log4j监视和筛选应用程序日志到邮件
  2. mongodb安装_MongoDB和Web应用程序
  3. ipmitool 设置网关_IPMI (Intelligent Platform Management Interface)
  4. html中两行三列怎样写,html – Flexbox 3 divs,两列,一列有两行
  5. mysql四个对勾_Mysql like查询语句中,结果包含反斜杠 \ 字符的,需要替换成四个反斜杠 \\\\...
  6. 南京农业大学计算机保研率,2016中国大学保研率排名出炉 江苏11所高校入百强...
  7. 绝了!这个开源验证码项目,差点晚上瘾...
  8. 字节跳动每一轮都会考算法吗?已拿Offer的兄弟分享经验!
  9. Spring Boot如何优雅的校验参数
  10. 死磕Java并发:Java内存模型之happens-before