前言

  python是一门很简单的脚本语言,他崇尚简洁,你喜欢数学,应该知道奥卡姆剃刀原理,python就一直信奉类似简单有效的哲学,最近几年特别火,我所接触的有用它来做机器人的开发的,有研究学术的用它来做科学计算的,以及现在很多新起的创业公司用它做web 开发的。不太清楚你们学金融的会用它来做啥,不过这不是重点,我介绍一下我所接触的python社区的情况,国内python社区主要是豆瓣,知乎这种具有工程师文化兼文艺气息的公司带起来的,社区涉及的技术方向也很广,基本上你能想到的都有,他最强大之处就是数据的处理效率特别高,封装了很多技巧。我可以直接用它的字典数据结构快速构建大矩阵,不用像matlab那么麻烦。下面是我搜集的资料,希望能够对你有帮助。

在线编辑器和执行环境

  如果你没有使用类unix操作系统的话是不会自带python的,你可以通过这些在线编辑器熟悉基本语法,开始都做不了啥,与其受装环境折磨不如现在这些编程网站上 面试一下。

入门书籍

入门视频资料

  慕课网是刚起来的一个IT教育平台,视频虽然比较废时间,但如果你不理解可以多看一下。

入门教程

  可能视频比较慢,你如果想快点就可以看这个在线教程。

推荐博客

  学了上面的部分,你可能就算入门了,下面是深入的教程。

书籍

python资料

github上资料

  • pycrumbs(这个讲了一些python中难理解的概念,比如装饰器与宏区别等.)
  • awesome-python(这个资料涉及很多方面,基本该有的都有.)

  在深入就是系统编程了,比如换python编译器,或者使用c编写扩展来提高他的性能。下面是python写入编译器里的箴言,你如果装了环境以后的话,输入import this就有(这个是许多python程序员专门用来装文艺翻译四字箴言用的,三字经版本)。

The Zen of Python, by Tim Peters

Beautiful is better than ugly.

Explicit is better than implicit.

Simple is better than complex.

Complex is better than complicated.

Flat is better than nested.

Sparse is better than dense.

Readability counts.

Special cases aren’t special enough to break the rules.

Although practicality beats purity.

Errors should never pass silently.

Unless explicitly silenced.

In the face of ambiguity, refuse the temptation to guess.

There should be one– and preferably only one –obvious way to do it.

Although that way may not be obvious at first unless you’re Dutch.

Now is better than never.

Although never is often better than right now.

If the implementation is hard to explain, it’s a bad idea.

If the implementation is easy to explain, it may be a good idea.

Namespaces are one honking great idea – let’s do more of those!

简介

  这是一篇王垠博客的文章,因为我从看他的博客重原来的CSDN到豆瓣,再到github开始一篇篇减少,现在他github上面的已经不能访问了,这是一篇个人觉得很不错的文章,所以转载到自己博客,免得丢失。原博客地址,但估计是域名没有续费,所以目前不能访问。学习程序语言是每个程序员的必经之路。可是这个世界上有太多的程序语言,每一种都号称具有最新的“特性”。所以程序员的苦恼就在于总是需要学习各种稀奇古怪的语言,而且必须紧跟“潮流”,否则就怕被时代所淘汰。

作为一个程序语言的研究者,我深深的知道这种心理产生的根源。程序语言里面其实有着非常简单,永恒不变的原理。看到了它们,就可以在很短的时间之内就能学会并且开始使用任何新的语言,而不是花费很多功夫去学习一个又一个的语言。

对程序语言的各种误解

  学习程序语言的人,经常会出现以下几种心理,以至于他们会觉得有学不完的东西,或者走上错误的道路。以下我把这些心理简要分析一下。

1.程序语言无用论。

  这是国内大学计算机系的教育常见的错误。教授们常常对学生灌输:“用什么程序语言不重要,重要的是算法。”而其实,程序语言却是比算法更加精髓的东西。任何算法以及它的复杂度分析,都是相对于某种计算模型,而程序语言就是描述这种计算模型的符号系统。算法必须用某种语言表述出来,通常算法设计者使用伪码,这其实是不严谨的,容易出现推理漏洞。算法设计再好,如果不懂得程序语言的原理,也不可能高效的实现。即使实现了,也可能会在模块化和可扩展性上面有很大问题。某些算法专家或者数学家写出来的程序极其幼稚,就是因为他们忽视了程序语言的重要性。

2.追求“新语言”

  基本的哲学告诉我们,新出现的事物并不一定是“新事物”,它们有可能是历史的倒退。事实证明,新出现的语言,可能还不如早就存在的。其 实,现代语言的多少“新概念”不存在于最老的一些语言里呢?程序语言就像商品,每一家都为了拉拢程序员作广告,而它们绝大多数的设计都可能是肤浅而短命的。如果你看不透这些东西的设计,就会被它们蒙蔽住。很多语言设计者其实并不真的懂得程序语言设计的原理,所以常常在设计中重复前人的错误。但是为了推销 自己的语言和系统,他们必须夸夸其谈,进行宗教式的宣传。

3.“存在即是合理”

  记得某人说过:“不能带来新的思维方式的语言,是没有必要存在的。”他说的是相当正确的。世界上有这么多的语言,有哪些带来了新的思维 方式呢?其实非常少。绝大部分的语言给世界带来的其实是混乱。有人可能反驳说:“你怎么能说 A 语言没必要存在?我要用的那个库 L,别的语言不支持,只能用A。”但是注意,他说的是存在的“必要性”。如果你把存在的“事实”作为存在的“必要性”,那就逻辑错乱了。就像如果二战时我们没能打败希特勒,现在都做 了他的奴隶,然后你就说:“希特勒应该存在,因为他养活了我们。”你的逻辑显然有问题,因为如果历史走了另外一条路(即希特勒不存在),我们会过上自由幸 福的生活,所以希特勒不应该存在。对比一个东西存在与不存在的两种可能的后果,然后做出判断,这才是正确的逻辑。按照这样的推理,如果设计糟糕的 A 语言不存在,那么设计更好的 B 语言很有可能就会得到更多的支持,从而实现甚至超越 L 库的功能。

4.追求“新特性”

  程序语言的设计者总是喜欢“发明”新的名词,喜欢炒作。普通程序员往往看不到,大部分这些“新概念”其实徒有高深而时髦的外表,却没有实 质的内涵。常常是刚学会一个语言A,又来了另一个语言 B,说它有一个叫 XYZ 的新特性。于是你又开始学习B,如此继续。在内行人看来,这些所谓的“新特性”绝大部分都是新瓶装老酒。很多人写论文喜欢起这样的标题:《XYZ:A Novel Method for …》。这造成了概念的爆炸,却没有实质的进步。

5. 追求“小窍门”

  很多编程书喜欢卖弄一些小窍门,教你如何让程序显得“短小”。比如它们会跟你讲 “(i++) - (++i)” 应该得到什么结果;或者追究运算符的优先级,说这样可以少打括号;要不就是告诉你“if 后面如果只有一行代码就可以不加花括号”,等等。殊不知这些小窍门,其实大部分都是程序语言设计的败笔。它们带来的不是清晰的思路,而是是逻辑的混乱和认 知的负担。比如 C 语言的 ++ 运算符,它的出现是因为 C 语言设计者们当初用的计算机内存小的可怜,而 “i++” 显然比 “i=i+1” 少 2 个字符,所以他们觉得可以节省一些空间。现在我们再也不缺那点内存,可是 ++ 运算符带来的混乱和迷惑,却流传了下来。现在最新的一些语言,也喜欢耍这种语法上的小把戏。如果你追求这些小窍门,往往就抓不住精髓。

6.针对“专门领域”

  很多语言没有新的东西,为了占据一方土地,就号称自己适合某种特定的任务,比如文本处理,数据库查询,WEB编程,游戏设计,并行计算。但是我们真的需要不同的语言来干这些事情吗?其实绝大部分这些事情都能用同一种通用语言来解决,或者在已有语言的基础上做很小的改动。只不过由于各种政治和商业原因,不同的语言被设计用来占领市场。就学习而言,它们其实是无关紧要的,而它们带来的“学习负担”,其实差不多掩盖了它们带来的好处。其实从一些设计良好的通用语言,你可以学会所有这些“专用语言”的精髓,而不用专门去学它们。

7.宗教信仰

  很多人对程序语言有宗教信仰。这跟人们对操作系统有宗教信仰很类似。其实如果你了解程序语言的本质,就会发现其实完全没必要跟人争论一些事情。某个语言有缺点,应该可以直接说出来,却被很多人忌讳,因为指出缺点总是招来争论和憎恨。这原因也许在于程序语言的设计不是科学,它类似于圣经,它没法被“证伪”。没有任何实验可以一下子断定那种语言是对的,那种是错的。所以虽然你觉得自己有理,却很难让人信服。没有人会去争论哪家的汉堡更好,却有很多人争论那种语言更好。因为很多人把程序语言当成自己的神,如果你批评我的语言,你就是亵渎我的神。解决的办法也许是,不要把自己正在用的语言看得太重要。你现在认为是对的东西,也许不久就会被你认为是错的,反之亦然。

如何掌握程序语言

看到了一些常见的错误心理,那么我们来谈一下什么样的思维方式会更加容易的掌握程序语言。

1.专注于“精华”和“原理”

  就像所有的科学一样,程序语言最精华的原理其实只有很少数几个,它们却可以被用来构造出许许多多纷繁复杂的概念。但是人们往往 忽视了简单原理的重要性,匆匆看过之后就去追求最新的,复杂的概念。他们却没有注意到,绝大部分最新的概念其实都可以用最简单的那些概念组合而成。而对基 本概念的一知半解,导致了他们看不清那些复杂概念的实质。比如这些概念里面很重要的一个就是递归。国内很多学生对递归的理解只停留于汉诺塔这样的程序,而 对递归的效率也有很大的误解,认为递归没有循环来得高效。而其实递归比循环表达能力强很多,而且效率几乎一样。有些程序比如解释器,不用递归的话基本没法完成。

2.实现一个程序语言

  学习使用一个工具的最好的方式就是制造它,所以学习程序语言的最好方式就是实现一个程序语言。这并不需要一个完整的编译器,而只需要写 一些简单的解释器,实现最基本的功能。之后你就会发现,所有语言的新特性你都大概知道可以如何实现,而不只停留在使用者的水平。实现程序语言最迅速的方式就是使用一种像 Scheme 这样代码可以被作为数据的语言。它能让你很快的写出新的语言的解释器。我的 GitHub 里面有一些我写的解释器的例子(比如这个短小的代码实现了 Haskell 的 lazy 语义)。

几种常见风格的语言

  下面我简要的说一下几种常见风格的语言以及它们的问题。

1. 面向对象语言

  事实说明,“面向对象”这整个概念基本是错误的。它的风靡是因为当初的“软件危机”(天知道是不是真的存在这危机)。 设计的初衷是让“界面”和“实现”分离,从而使得下层实现的改动不影响上层的功能。可是大部分面向对象语言的设计都遵循一个根本错误的原则:“所有的东西 都是对象(Everything is an object)。”以至于所有的函数都必须放在所谓的“对象”里面,而不能直接被作为参数或者变量传递。这导致很多时候需要使用繁琐的设计模式 (design patterns) 来达到甚至对于 C 语言都直接了当的事情。而其实“界面”和“实现”的分离,并不需要把所有函数都放进对象里。另外的一些概念,比如继承,重载,其实带来的问题比它们解决的 还要多。 “面向对象方法”的过度使用,已经开始引起对整个业界的负面作用。很多公司里的程序员喜欢生搬硬套一些不必要的设计模式,其实什么好事情也没干,只是使得程序冗长难懂。 那么如何看待具备高阶函数的面向对象语言,比如 Python, JavaScript, Ruby, Scala? 当然有了高阶函数,你可以直截了当的表示很多东西,而不需要使用设计模式。但是由于设计模式思想的流毒,一些程序员居然在这些不需要设计模式的语言里也采用繁琐的设计模式,让人哭笑不得。所以在学习的时候,最好不要用这些语言,以免受到不必要的干扰。到时候必要的时候再回来使用它们,就可以取其精华,去其糟粕。

2. 低级过程式语言

  那么是否 C 这样的“低级语言”就会好一些呢?其实也不是。很多人推崇 C,因为它可以让人接近“底层”,也就是接近机器的表示,这样就意味着它速度快。这里其实有三个问题: 1) 接近“底层”是否是好事? 2)“速度快的语言”是什么意思? 3) 接近底层的语言是否一定速度快? 对于第一个问题,答案是否定的。其实编程最重要的思想是高层的语义(semantics)。语义构成了人关心的问题以及解决它们的算法。而具体的实现 (implementation),比如一个整数用几个字节表示,虽然还是重要,但却不是至关重要的。如果把实现作为学习的主要目标,就本末倒置了。因为 实现是可以改变的,而它们所表达的本质却不会变。所以很多人发现自己学会的东西,过不了多久就“过时”了。那就是因为他们学习的不是本质,而只是具体的实 现。 其次,谈语言的“速度”,其实是一句空话。语言只负责描述一个程序,而程序运行的速度,其实绝大部分不取决于语言。它主要取决于 1)算法 和 2)编译器的质量。编译器和语言基本是两码事。同一个语言可以有很多不同的编译器实现,每个编译器生成的代码质量都可能不同,所以你没法说“A 语言比 B 语言快”。你只能说“A 语言的 X 编译器生成的代码,比 B 语言的 Y 编译器生成的代码高效”。这几乎等于什么也没说,因为 B 语言可能会有别的编译器,使得它生成更快的代码。 我举个例子吧。在历史上,Lisp 语言享有“龟速”的美名。有人说“Lisp 程序员知道每个东西的值,却不知道任何事情的代价”,讲的就是这个事情。但这已经是很久远的事情了,现代的 Lisp 系统能编译出非常高效的代码。比如商业的 Chez Scheme 编译器,能在5秒钟之内编译它自己,编译生成的目标代码非常高效。它可以直接把 Scheme 程序编译到多种处理器的机器指令,而不通过任何第三方软件。它内部的一些算法,其实比开源的 LLVM 之类的先进很多。 另外一些 函数式语言也能生成高效的代码,比如 OCaml。在一次程序语言暑期班上,Cornell 的 Robert Constable 教授讲了一个故事,说是他们用 OCaml 重新实现了一个系统,结果发现 OCaml 的实现比原来的 C 语言实现快了 50 倍。经过 C 语言的那个小组对算法多次的优化,OCaml 的版本还是快好几倍。这里的原因其实在于两方面。第一是因为函数式语言把程序员从底层细节中解脱出来,让他们能够迅速的实现和修改自己的想法,所以他们能 够迅速的找到更好的算法。第二是因为 OCaml 有高效的编译器实现,使得它能生成很好的代码。 从上面的例子,你也许已经可以看出,其实接近底层的语言不一定速度就快。因为编译器这种东西其实可以有很高级的“智能”,甚至可以超越任何人能做到的底层优化。但是编译器还没有发展到可以代替人来制造算法的地步。所以现在人需要做的,其实只是设计和优化自己的高层算法。

3. 高级过程式语言

  很早的时候,国内计算机系学生的第一门编程课都是 Pascal。Pascal 是很不错的语言,可是很多人当时都没有意识到。上大学的时候,我的 Pascal 老师对我们说:“我们学校的教学太落后了。别的学校都开始教 C 或者 C++ 了,我们还在教 Pascal。”现在真正理解了程序语言的设计原理以后我才真正的感觉到,原来 Pascal 是比 C 和 C++ 设计更好的语言。它不但把人从底层细节里解脱出来,没有面向对象的思维枷锁,而且有一些很好的设计,比如强类型检查,嵌套函数定义等等。可是计算机的世界 真是谬论横行,有些人批评 Pascal,把优点都说成是缺点。比如 Brain Kernighan 的这篇《Why Pascal is Not My Favorite Programming Language》,现在看来真是谬误百出。Pascal 现在已经几乎没有人用了。这并不很可惜,因为它被错怪的“缺点”其实已经被正名,并且出现在当今最流行的一些语言里:Java, Python, C#, ……

4. 函数式语言

  函数式语言相对来说是当今最好的设计,因为它们不但让人专注于算法和对问题的解决,而且没有面向对象语言那些思维的限制。但是需要注意的是并不是每个函数式语言的特性都是好东西。它们的支持者们经常把缺点也说成是优点,结果你其实还是被挂上一些不必要的枷锁。比如OCaml 和 SML,因为它们的类型系统里面有很多不成熟的设计,导致你需要记住太多不必要的规则。

5.逻辑式语言

  逻辑式语言(比如 Prolog)是一种超越函数式语言的新的思想,所以需要一些特殊的训练。逻辑式语言写的程序,是能“反向运行”的。普通程序语言写的程序,如果你给它一个输入,它会给你一个输出。但是逻辑式语言很特别,如果你给它一个输出,它可以反过来给你所有可能的输入。其实通过很简单的方法,可以不费力气的把程序从函数式转换成逻辑式的。但是逻辑式语言一般要在“pure”的情况下(也就是没有复杂的赋值操作)才能反向运行。所以学习逻辑式语言最好是从函数式语言开始,在理解了递归,模式匹配等基本的函数式编程技巧之后再来看 Prolog,就会发现逻辑式编程简单了很多。

从何开始

  可是学习编程总要从某种语言开始。那么哪种语言呢?就我的观点,首先可以从 Scheme 入门,然后学习一些 Haskell(但不是全部),之后其它的也就触类旁通了。你并不需要学习它们的所有细枝末节,而只需要学习最精华的部分。所有剩余的细节,会在实际使用中很容易的被填补上。现在我推荐几本比较好的书。 《The Little Schemer》(TLS):我觉得 Dan Friedman 的 The Little Schemer 是目前最好,最精华的编程入门教材。这本书很薄,很精辟。它的前身叫《The Little Lisper》。很多资深的程序语言专家都是从这本书学会了 Lisp。虽然它叫“The Little Schemer”,但它并不使用 Scheme 所有的功能,而是忽略了 Scheme 的一些毛病,直接进入最关键的主题:递归和它的基本原则。 《Structure and Interpretation of Computer Programs》(SICP):The Little Schemer 其实是比较难的读物,所以我建议把它作为下一步精通的读物。SICP 比较适合作为第一本教材。但是我需要提醒的是,你最多只需要看完前三章。因为从第四章开始,作者开始实现一个Scheme解释器,但是作者的实现并不是最好的方式。你可以从别的地方更好的学到这些东西。不过也许你可以看完 SICP 第一章之后就可以开始看 TLS。 《A Gentle Introduction to Haskell》:对于 Haskell,我最开头看的是 A Gentle Introduction to Haskell,因为它特别短小。当时我已经会了Scheme,所以不需要再学习基本的函数式语言的东西。我从这个文档学到的只不过是 Haskell 对于类型和模式匹配的概念。

过度到面向对象语言

  那么如果从函数式语言入门,如何过渡到面向对象语言呢?毕竟大部分的公司用的是面向对象语言。如果你真的学会了函数式语言,就会发现面向对象语言已经易如反掌。函数式语言的设计比面向对象语言简单和强大很多,而且几乎所有的函数式语言教材(比如 SICP)都会教你如何实现一个面向对象系统。你会深刻的看到面向对象的本质以及它存在的问题,所以你会很容易的搞清楚怎么写面向对象的程序,并且会发现 一些窍门来避开它们的局限。你会发现,即使在实际的工作中必须使用面向对象语言,也可以避免面向对象的思维方式,因为面向对象的思想带来的大部分是混乱和冗余。

深入本质和底层

  那么是不是完全不需要学习底层呢?当然不是。但是一开头就学习底层硬件,就会被纷繁复杂的硬件设计蒙蔽头脑,看不清楚本质上简单的原理。在学会高层的语言之后,可以进行“语义学”和“编译原理”的学习。   简言之,语义学(semantics) 就是研究程序的符号表示如何对机器产生“意义”,通常语义学的学习包含 lambda calculus 和各种解释器的实现。编译原理 (compilation)就是研究如何把高级语言翻译成低级的机器指令。编译原理其实包含了计算机的组成原理,比如二进制的构造和算术,处理器的结构,内存寻址等等。但是结合了语义学和编译原理来学习这些东西,会事半功倍。因为你会直观的看到为什么现在的计算机系统会设计成这个样子:为什么处理器里面有寄存器(register),为什么需要堆栈(stack),为什么需要堆(heap),它们的本质是什么。这些甚至是很多硬件设计者都不明白的问题,所以它们的硬件里经常含有一些没必要的东西。因为他们不理解语义,所以经常不明白他们的硬件到底需要哪些部件和指令。但是从高层语义来解释它们,就会揭示出它们的本质,从而可以让你明白如何设计出更加优雅和高效的硬件。这就是为什么一些程序语言专家后来也开始设计硬件。比如 Haskell 的创始人之一 Lennart Augustsson 后来设计了 BlueSpec,一种高级的硬件描述语言,可以 100% 的合成(synthesis) 为硬件电路。Scheme 也被广泛的使用在硬件设计中,比如 Motorola, Cisco 和曾经的 Transmeta,它们的芯片设计里面含有很多 Scheme 程序。

简介

  并发FIFO队列在并行程序以及操作系统中有广泛的应用,为了确保程序的正确性,在并发访问共享队列的时候必须进行同步化。举个例子,比如在生产者与消费者模型的应用中,在没有产品时,消费者就必须要等到生产者生产出产品后才能运行。对于像FIFO队列这样的并发数据结构的算法,一般分成两种:阻塞和非阻塞,阻塞算法使用一个延迟的进程去避免更快进程在不确定的共享内存中完成操作,这个延迟同步进程可以看成锁。非阻塞算法保证在一个或多个活跃进程尝试操作共享数据结构的时候,某些操作将在有限的时间周期内完成,不会发生因为像抢锁所形成的死锁情况。在同步多进程系统中,尤其是多进程的情况,阻塞算法中,当一个进程在不适当的时间被挂起或延迟的时候,阻塞算法的性能将会严重下降。延迟可能的原因有进程的抢占式调度,页面中断(引起页面置换),以及缓存未命中。但是,在面对以上问题,非阻塞算法鲁棒性更好,更健壮。

  许多研究者对于并发FIFO队列使用无锁算法,Hwang和Briggs,Stong,Sites均描述了无锁算法基于compare-and-swap的实现(把共享内存地址,一个保留值,以及一个 新值当作参数,如果共享内存中目前保存着保留值,那么它将被标记为新值,返回一个布尔值标记是否发生交换,显然这个预留值的设定也是一门学问)。但这个算法不是特别详细,他们 省略了一些详细内容,比如处理单元素队列,空队列以及并发的出队,入队行为。Lamport描述了一个无等待算法(非阻塞和无饥俄性算法,该算法保证每一个活动进程在有限的时间片内得到处理),这个算法使用入队器和出队器限制并发。

  Gottlieb和Mellor-Crummey描述了一个无锁但是不是非阻塞的算法:他们不使用锁的机制,但是他们允许一个慢的进程去延迟不确定的较快进程。

  Treiber描述了一个非阻塞的算法,但是效率低,一个出队操作花费的时间与队列元素个数成正比.许多专家等提议使用 一般的方法,对于非阻塞算法,使用版本序列或者基于锁控制来实现。通常而言,这种的效果相比特殊方法是低效率的。

  Massalin和Pu描述了一种基于double-compare-and-swap的无锁算法,该算法可以同时操作两个任意的内存位置,但是似乎只能在Motorola 68000之后的处理器才能使用, Herlihy和Wing描述了一种基于数组的算法,但是该算法对于数组的需求不确定。Valois描述了一种基于数组的算法,需要一个未对齐的compare-and-swap或者double-compare-and-swap`。

  Stone描述了一个无锁算法,但是它是非线性(如果在数据结构之外给一个外部的观察者,观察对数据结构的操作,在某时刻对它的调用和响应都能观察到立即产生的影响,就说这个数据结构的实现是可线性的)和阻塞的。因其是非线性的,所以当一个慢的入队器观察到一个空的队列,但是某个快的入队操作已经随后加入了一个元素,甚至可能出现这个已经入队元素永远不出队。因为一个慢的入队操作能够通过其他不确定的进程延迟出队操作,所以它也是非阻塞的。我们的实验同时也揭露存在竞争条件,比如一个慢的入队操作,以及一个快的入队操作和一个快的出队操作,将可能造成某个入队元素丢失(快的出队操作已经执行,慢的出队操作又执行了一次,所以造成了某个入队的元素被莫名其妙移除),这就是两个出队操作存在竞争的原因。Stone也描述了一个基于循环单链表的队列,该算法使用锚点指针代替头尾指针管理队列,我们的实验同样揭露存在一个竞争条件:慢的出队操作进程能造成入队元素永久丢失。

   Prakash,Lee 和Johnson描述了一种线性非阻塞算法,它需要入队进程和出队进程在决定更新队列的状态之前获取它的一个快照,这个算法通过允许较快进程去完成(而不是等待)较慢进程的操作代 替阻塞来达到非阻塞的性能。   Valois描述了一种基于列表的非阻塞算法,为了避免Prakash等人的快照算法引起的争论, 该算法通过在单向链表的头部(出队端)添加一个虚拟节点来允许更多的并发,因此也简化了单元素队列和空队列的情况。但是,该算法为了安全的释放和重用出队节点允许尾指针指向头指针,如果一个尾指针指向一个出队节点,但这个出队节点被一进程释放,这个链表将被截断,随后入队的元素都会丢失。由于内存是有限的资源,禁止内存重新使用是不现实的。Valois因此建议一个特殊的机制去释放和分配内存,该机制对每一个节点使用了一个引用计数器,每当进程创建一个指针指向节点时,该节点的引用计数器自动加1。在不打算访问这个被访问过节点以后,他的引用计数器会自动的减少。除此之外,还有来自进程局部变量的临时链接,每一个引用计数器反映了在这个问题中指向这个数据结构节点的链接数。 对于队列,他们是头指针,尾指针,以及链表的链接数。当一个节点没有指针或者临时变量指向他的时候,该节点将被释放。


未完待续

前言

  该篇文章是来自于stackoverflow上的一个问题,原文地址,个人觉得很不错,就将它翻译了过来。可能有些单词翻译的不太准确,欢迎矫正。

运算符的链式比较

Example:
    >>> x = 5
    >>> 1 < x < 10
    True
    >>> 10 < x < 20 
    False
    >>> x < 10 < x*10 < 100
    True
    >>> 10 > x <= 9
    True
    >>> 5 == x > 4
    True

  在这个例子中,你可能认为首先运算 1<x,得到结果为 True ,接着运算 True < 10 ,得到结果为 True .但是,事实并非如此,看最后一个表达式计算,你就知道并非这样,比如第一个表达式,他其实是转化成了 1<x and x < 10 .第二个表达式转化成了 x<10 and 10<x10 and x10<100 ,为了更少的打印,每个表达式只计算了一次(告诉你,自己玩吧)。评论里有提到,lisp也是这种类似的机制。

函数参数解包

  使用***作 为函数参数时,你可以传入list和dict类型数据,意思就是当你使用 * 前缀时可以代表list,tuple等序列数据,而 ** 前缀可以表示字典类型数据。 例子:

def draw_point(x, y):
        # do some magic

        point_foo = (3, 4)
        point_bar = {'y': 3, 'x': 2}

        draw_point(*point_foo)
        draw_point(**point_bar)

字典、列表被广泛用于容器后,非常方便。

就地交换

for example:
    >>> a = 10
    >>> b = 5
    >>> a, b
    (10, 5)

    >>> a, b = b, a
    >>> a, b
    (5, 10)

这个指令(a,b = b,a)右边的表达式创建了一个新元组,左边的立马解包到变量a,b。在这条指令过后,a,b的值已经被交换,而 a,b这个新元祖也未被GC标记和引用,所以未被回收。

for…else语句

  

for i in foo:
        if i == 0:
            break
    else:
        print('i was never zero.)

这个程序在 break 没有被调用的时候会执行,这段代码还可以使用下面这段通俗代码来理解:

found = False
    for i in foo:
        if i == 0:
            found = True
            break
    if not found:
        print('i was never zero.')

不过这个特点还是少用比较好,放在for循环内部就可以了,放在外部有可能和其他的if语句引起bug.

指定名字的格式化

%可以像字典一样对参数进行格式化:

for example:
    >>> print "The %(foo)s is %(bar)i." % {'foo': 'answer', 'bar':42}
    The answer is 42.

    >>> foo, bar = 'question', 123

    >>> print "The %(foo)s is %(bar)i." % locals()
    The question is 123.

locals()也是字典,你可以使用locals()% 替换本地变量。

新风格的格式化:

>>> print("The {foo} is {bar}".format(foo='answer', bar=42))

推荐新风格,新风格跟有pythonic的感觉。

使用.pth文件添加路径

  添加python包路径的最好办法是使用.pth文件进行添加路径,每一行只能包含一条路径,这些路径会添加到sys.path中,而且安装的目录不会覆盖原来的标准模块,这个机制意味着你不能安装标准模块的固定版本(因为都会存在).

set内置的操作符重载

  集合类型内置了许多操作符号的重载,比如 & , | 等。

for example:
        >>> a = set([1,2,3,4])
        >>> b = set([3,4,5,6])
        >>> a | b # Union
        {1, 2, 3, 4, 5, 6}
        >>> a & b # Intersection
        {3, 4}
        >>> a < b # Subset
        False
        >>> a - b # Difference
        {1, 2}
        >>> a ^ b # Symmetric Difference
        {1, 2, 5, 6}

嵌套的列表表达式和生成表达式

for example:
    [(i,j) for i in range(3) for j in range(i) ]    
    ((i,j) for i in range(4) for j in range(i) )

  python的列表表达式可以生成一个值的列表,这个好理解,而生成表达式是会产生一系列的表达式,举个例子,比如 square = (i2+2 for i in 3),最终会生成(02+2),(12+2),(22+2),生成表达式的思想就是协程的yield/resume机制。关于协程的概念,戳这里。对于生成器的优点就是 他不会产生中间存储值,相对于列表表达式使用更少的内存,而列表是一次生成存储之后再使用,在某些情况下,列表表达式相对于生成器表达式速度更快。在循环中,你可以使用一个生成器替换许多嵌套:

for example:
        >>> n = ((a,b) for a in range(0,2) for b in range(4,6))
        >>> for i in n:
        ...   print i 

        (0, 4)
        (0, 5)
        (1, 4)
        (1, 5)

枚举计算

  通过枚举包装一个迭代器,他将会产生和索引一起的元素。

for example:
        >>> a = ['a', 'b', 'c', 'd', 'e']
        >>> for index, item in enumerate(a): 
                print index, item
        ...
        0 a
        1 b
        2 c
        3 d
        4 e

同时,枚举还可以指定开始位置.

for example:
        for index,item in enumerate(a, start=2)

iter()能够携带一个可调用的参数

for instance:
        def seek_next_line(f):
        for c in iter(lambda : f.read(1),"\n"):
            pass

iter是一个迭代器,iter(callable, sentinel),被理解为被调用的参数是在哨兵被返回时调用。


  未完待续。

前言

  昨天在ptipython下输入urllib代码补全时出现提示有一个urllib3的python包,可能是某些应用使用的依赖包,于是在pip搜索了一下,github地址,看到官方介绍,貌似很酷炫的样子:称该包是一个高性能,线程安全,且能重用tcp连接的库。

测试

  根据官方的benchmark test我添加了urllib2的测试,为了体现urllib3的重用连接优点,我特意找了一个接口,能够让urllib3最大限度重用tcp连接,测试demo如下:

#!/usr/bin/env python
	from __future__ import print_function
	import sys
	import time
	import urllib
	import urllib2

	sys.path.append('../')
	import urllib3


	# URLs to download. Doesn't matter as long as they're from the same host, so we
	# can take advantage of connection re-using.
	TO_DOWNLOAD = [
	    'http://www.gandong.com/xml/132.xml',
	    'http://www.gandong.com/xml/135.xml',
	    'http://www.gandong.com/xml/134.xml',
	    'http://www.gandong.com/xml/136.xml',
	    'http://www.gandong.com/xml/137.xml',
	    'http://www.gandong.com/xml/138.xml',
	    'http://www.gandong.com/xml/139.xml',
	    'http://www.gandong.com/xml/151.xml',
	    'http://www.gandong.com/xml/152.xml',
	    'http://www.gandong.com/xml/153.xml',
	    'http://www.gandong.com/xml/154.xml',
	    'http://www.gandong.com/xml/156.xml',
	    'http://www.gandong.com/xml/158.xml',
	    'http://www.gandong.com/xml/163.xml',
	    'http://www.gandong.com/xml/164.xml',
	]


	def urllib_get(url_list):
	    assert url_list
	    for url in url_list:
		now = time.time()
		r = urllib.urlopen(url)
		elapsed = time.time() - now
		print("Got in %0.3f: %s" % (elapsed, url))

	def urllib2_get(url_list):
	    assert url_list
	    for url in url_list:
		now = time.time()
		r = urllib2.urlopen(url)
		elapsed = time.time() -now
		print("Got in %0.3f: %s" % (elapsed, url))

	def pool_get(url_list):
	    assert url_list
	    pool = urllib3.PoolManager()
	    for url in url_list:
		now = time.time()
		r = pool.request('GET', url, assert_same_host=False)
		elapsed = time.time() - now
		print("Got in %0.3fs: %s" % (elapsed, url))


	if __name__ == '__main__':
	    print("Running pool_get ...")
	    now = time.time()
	    pool_get(TO_DOWNLOAD)
	    pool_elapsed = time.time() - now

	    print("Running urllib_get ...")
	    now = time.time()
	    urllib_get(TO_DOWNLOAD)
	    urllib_elapsed = time.time() - now

	    print("Running urllib2_get ...")
	    now = time.time()
	    urllib2_get(TO_DOWNLOAD)
	    urllib2_elapsed = time.time() - now

	    print("Completed pool_get in %0.3fs" % pool_elapsed)
	    print("Completed urllib_get in %0.3fs" % urllib_elapsed)
	    print("Completed urllib2_get in %0.3fs" % urllib_elapsed)
第一次结果:
    Completed pool_get in 0.504s
    Completed urllib_get in 0.323s
    Completed urllib2_get in 0.323s


     第二次结果:
     Completed pool_get in 0.317s
     Completed urllib_get in 0.457s
     Completed urllib2_get in 0.457s

  我的测试网络稳定,反而在第一次时urllib3慢于urllib2和urllib,明显urllib和urllib2在抓取上机制相同。我多次测试后,上述结果基本保持稳定。可见,urllib3并没有官方说的那么高性能。只能说明该包对于重用tcp连接的重用率极低。在速度上面并没有明显的提升。

总结

  相对于urllib,urllib3并没有在速度上有所提升,反而更慢,不过其官方特性说使用了安全连接,这点没有去测试,如果描述真实的话,对于学校的一些模拟登录来说,可能会更适用一点。对一些基本的网络抓取,还是使用urllib更快速。不过对于urllib的设计来说,还是有一个可取之处,类风格使用新式类,语义描述更明确直观,返回的对象是response等。