赖勇浩(http://laiyonghao.com)

第二贴

在 2.2 节函数声明中,DIP 讲述了关于函数定义的一些基础知识,后来又在 4.2 节使用可选参数和命名参数中讲述了关于函数参数的较为深入的话题。函数相关的内容分到两个不同的章节,DIP 的内容编排的确是出常人所料,不过今天我们不讨论这个,闲话少说,进入关于函数的更多正题。

不定参数

在 C/C++ 中,不定参数可以算得上一节提高篇的课程。因为它的 va_list、va_start和 va_end 等是侵入式的,理解起来并不容易;此外由于 C/C++ 都是静态强类型语言,在运行时数据并不携带类型信息,因此不定参数函数更像是一个调用协议,需要函数定义者和使用者之间通过文档、注释等方式进行沟通;或者像 printf() 函数那样用 fmt 参数隐式指出参数类型,然后进行显式转型。

不定参数在 Python 中则简单得多。再回过头来年一下 C/C++,其实 va_list,完全是一个 tuple 实现,因为它可以持有不同数据类型的指针(通过void* 来实现)。得益于 Python 函数调用时的 boxing 和 unboxing 操作,Python 可以为不定参数的存取提供更为简洁的实现。如:

def foo(*args):
  for arg in args: print arg

在 Python 中可以使用 *args 语法为函数定义不定参数,其中 args 相当于 C/C++ 的 va_list,它是一个 tuple 类型的参数容器,所以无需所谓的 va_start、va_end 就可以简单遍历所有参数了。

在 Python 中,不定参数可以直接用 tuple 参数调用,如:

names = ('laiyonghao', 'denggao', 'liming')

foo(*names) # 留意符号 *

关键字参数

尽管不定参数给函数带来了很多便利性,但 Python 的关键字参数尤为神通广大。关键字参数是指以下形式定义的参数:

def foo(**kw): pass

其中 kw 本质上是一个 dict 对象,所以可以这样调用 foo:

foo( **{'a' : 1, 'b' : 2, 'c' : 3} )

看起来有点眼熟?对的,在“第一贴”(http://blog.csdn.net/lanphaday/archive/2008/08/31/2857813.aspx)里 DIP 的例 2.1 就有这几行代码:

if __name__ == "__main__":

myParams = {"server":"mpilgrim",

"database":"master",

"uid":"sa",

"pwd":"secret"

}

print buildConnectionString(myParams)

这个 buildConnectionString(myParams) 和前文的 foo() 调用很像吧,而且利用关键字参数后更复杂了。其实不是这样的,如果使用关键字参数,例2.1 可以写得优为简洁:

def buildConnectionString(**params):

"""Build a connection string from a dictionary of parameters.

Returns string."""

return ";".join("%s=%s" % (k, v) for k, v in params.iteritems())

if __name__ == "__main__":

print buildConnectionString(

server = ‘mpilgrim’,

database = ‘master’

uid = ‘sa’

pwd = ‘secret’)

除了更加优雅外,也比以前一种写法提升性能呢。

可调用对象

在 C++ 中有一种对象被称为仿函数(functor),因为它重载了 operator(),参数模仿函数调用。得益于 inline 关键字,仿函数可以减少函数调用,提升性能,所以在 C++ 的世界里仿函数是非常重要的。

尽管 Python 中没有类似 inline 的关键字,但因为有不少函数需要自身保留一些“状态”,所以仍然实现了仿函数,在 Python 中称为可调用对象。

狭义来讲,可调用对象是拥有 __call__ 方法的类实例,如:

>>> class Callable(object):

...     def __call__(self):

...             print 'callable'

...

>>> call_obj = Callable()

>>> call_obj()

decorator

定义一个函数在大多数时候都很简单,但事实上 def 语句是相当复杂的,不信你看看它的语法:

funcdef ::= [decorators] "def" funcname "(" [parameter_list] ")" ":" suite

decorators ::= decorator+

decorator ::= "@" dotted_name ["(" [argument_list [","]] ")"] NEWLINE

dotted_name ::= identifier ("." identifier)*

parameter_list ::= (defparameter ",")*

(  "*" identifier [, "**" identifier]

| "**" identifier

| defparameter [","] )

defparameter ::= parameter ["=" expression]

sublist ::= parameter ("," parameter)* [","]

parameter ::= identifier | "(" sublist ")"

funcname ::= identifier

是不是开始有懵的感觉了?

其实没有多大关系啦,大部分你都已经掌握了的,唯一可能还不太了解的可能就是 decorator(s),其实 decorator 的理念很简单:就是把一个函数“改头换面”之后返回给大家,让大家仍然可以用原来定义的函数名来调用这个返回的新函数。有点拗口?那看个例子就好了,比如有个王地主,出了名的小气,小气到鸡蛋过手都要小一圈:

>>> def wang(func):

...     def my_func(n, w):

...             if n == "wang":

...                     return func(n, w * 0.9)

...             return func(n, w)

...     return my_func

...

>>> @wang

... def transfer(name, weight):

...     print "name = %s, weight = %s"%(name, str(weight))

...

>>> transfer("lai", 100.0)

name = lai, weight = 100.0

>>> transfer("wang", 100.0)

name = wang, weight = 90.0

怎么样,看看输出,有点神奇吧,我过手的鸡蛋还是 100 克的,王地主就不一样了,只剩 90 克,真是高招儿……

其实 decorator 的本事这止这些,下面的代码是从手册里抄出来的:

@f1(arg)

@f2

def func(): pass

上面的代码等效于:

def func(): pass

func = f1(arg)(f2(func))

怎么样?decorator 可带参数还可以嵌套,具体的用法就请 RTFM 了。最后透一点剧情,decorator 在 py3.0 中也开始可以用来修饰类的定义了,不过这个内容就等给 DIP 打 py3.0 的补丁的时候再谈了。

-------------------------------------------

咳,不好意思,时隔 8 个多月之后,《<Dive into Python>大补贴》终于推出第二贴。关于 DIP 的其它讨论请见这儿:http://blog.csdn.net/lanphaday/category/454256.aspx。

<Dive into Python>大补贴(2)相关推荐

  1. <Dive into Python>大补贴(1)

    <Dive into Python>大补贴 作者:赖勇浩 前言 前几日发了一篇<为什么<Dive into Python>不值得推荐>的贴子,有朋友指出这本书虽然不 ...

  2. <Dive into Python>大补贴

    作者:赖勇浩 前言 前几日发了一篇<为什么<Dive into Python>不值得推荐>的贴子,有朋友指出这本书虽然不适合初学者,但里面的一些内容还是可以当作提高篇的,最大的 ...

  3. Dive into Python

    写这篇文章的原因完全是为了督促自己每天晚上看完两章<Dive Into Python>这本书,因此,很多内容都是摘抄自原书的翻译版或者是自己瞎想,于是就顺带着记录了下来.此前已经看完前两章 ...

  4. 《Dive Into Python》非死不可

    译者言: 早在 2008 年 8 月,我就曾在自己的博客发表了一篇<为什么<Dive into Python>不值得推荐>(http://blog.csdn.net/lanph ...

  5. 为什么《Dive into Python》不值得推荐

    2010 年 5 月 5 日更新:我翻译了一篇<<Dive Into Python>非死不可>作为对本文观点的进一步支持和对评论的回复,请见:http://blog.csdn. ...

  6. 深入python3 (Dive Into Python 3) 在线阅读与下载

    在线阅读:http://book.doucube.com/diveintopython3/  中文版 下载地址:https://github.com/downloads/diveintomark/di ...

  7. 深入 Python :Dive Into Python 中文版 读书笔记 第13,14,15单元测试

    2019独角兽企业重金招聘Python工程师标准>>> 第 13 章 单元测试 13.4. 正面测试 (Testing for success) 13.5. 负面测试 (Testin ...

  8. dive into python 3_对象方法Dive into Python读书笔记3

    近期朋友几篇文章介绍了改对象方法的文章. 关联文章的地址 XML,以及网络部份直接跳过了. 单元测试部份很好,但是我前目是小作坊式的发开,更重视效率,所以也略过. 当前有机遇 会返来看这几章的. 1. ...

  9. 对象方法Dive into Python读书笔记3

    近期朋友几篇文章介绍了改对象方法的文章. 关联文章的地址 XML,以及网络部份直接跳过了. 单元测试部份很好,但是我前目是小作坊式的发开,更重视效率,所以也略过. 当前有机遇 会返来看这几章的. 1. ...

最新文章

  1. 最强骨干网 ResNeSt 助力语义分割,ADE20K 全新 SOTA 47.6%
  2. 从零开始学python网络爬虫-教你从零开始学会写爬虫(Python)
  3. MATLAB AWGN信道 M-ary PSK 误码性能
  4. 【NLP】NLP提效,除了选择合适的模型,就是数据增强了
  5. python3super用法_Python3中的super()函数详解
  6. OSG的垃圾回收机制
  7. C++ _countf
  8. 美团内推:java高级开发(一面+二面+三面),面试58题实拍!
  9. php mysql分页_PHP+Mysql实现分页
  10. Python金融数据挖掘 第11章 复习思考题2 (聚类)选取中华人民共和国第六次人口普查的各地区人口数以及男女比例进行K-Means聚类分析。
  11. 两个月快速通过软考高项(信息系统项目管理师)备考技巧
  12. PhotoShop .psd文件格式读取分析(结合unity)
  13. 4.2.6 图层、通道和蒙版
  14. QQ聊天记录恢复、迁移教程(改变默认存储位置、个人文件夹保存位置)【转载】
  15. 科目二 离合 要点记录
  16. 苹果无线耳机连接不上_为什么我们一定要买TWS真无线耳机?
  17. 【python机器学习】普通最小二乘法多元线性回归
  18. U盘图标更改 简单三步教你个性化定制U盘图标!自定义修改你的U盘
  19. openCV calcHist函数的使用
  20. python——pandas展示所有列

热门文章

  1. 2015年CVTE校园招聘--Web后台服务开发工程师笔试题目及参考答案
  2. ★C/C++语言期末课程设计★——万年历显示系统(详细报告+源代码+详细注释)
  3. 360安全卫士不能使用
  4. 联想Lenovo 小新Pro-13 AIR 2020 Windows 10 间歇性 蓝屏 的解决方法 : 又一个AI变成 人工智障的失败案例?
  5. Bug 20250147 - ORA-600 [kjxmgmb_nreq:bat]
  6. django restframework choice 自定义输出数据
  7. 专题学习Linux笔记
  8. APS中生产计划排程模块的基本原理
  9. 【工具】Vue中生成二维码组件——vue-qr
  10. Adobenbsp;Creativenbsp;Suitenbsp;6(CS6)正…