昨天的文章收获了不少有价值的回复。不少人发现了一个大bug,那就是「上帝的归上帝,撒旦的归撒旦」。囧死我了。脑手不同步这病怎么治啊~以后我写完文章争取好好复查一遍。
有个名叫「舟」的读者写了段很棒的评论,不敢独专,和大家分享:
几点看法,随便谈谈:1.应该是“上帝的归上帝,凯撒的归凯撒”,原意讲的是宗教和世俗的关系,很深刻,变成“撒旦的归撒旦”以后这句话的意思其实就很让人费解了… 2. IoC的确是个很好的东西,但我认为它和libc中那种供应用层调用的函数之间并不是一种简单的进化关系,二者是互补的缺一不可,从两个不同方向减少软件的重复。libc中其实也有IoC的东西,例如qsort。libc毕竟是一个底层库提供的是应用和系统的接口,所以不需要太多IoC的东西。不过IoC确实比那种简单供应用调用的函数更深奥一点,新手更不容易掌握一点,所以更值得拿出来说。3. 不觉得AOP与OOP/FP是一个量级的东西,AOP在OOP中完全可以通过Decorator模式解决,在FP中就更直接了当了,python中函数的decorator就是一种典型的高阶函数,是FP的东西。 今天讨论的这些东西其实都是一言难尽的,随便一段单独拿出来都够程序君写一整篇文章的了,哈哈
这个周末要给人培训python,所以干脆今天先讲讲python,作为预演。我之前的文章也在不同的场合都建议初学者学习python。原因在于python良好的文化,和丰富的应用场景。当然,很多我接触过的语言都很好,学起来也很有滋味,但是。。。谁让有句俗语说:「真正的程序员用C,聪明的程序员用python」呢。
(今天的文字最好还是横过来看)
请打开任何一个python解释器,在里面输入:import this
。我所了解的任何一门其他语言都没有如此巧妙而又大张旗鼓地将其文化写入了语言本身。
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!
这段文字值得好好消化。你一定会好奇,this
究竟是什么?如果你对python的package机制有了解,那么,你就应该知道该从python的安装路径下找这个源码this.py
,打开一看,一段格式熟悉,但乱七八糟的文字,然后是一段代码:
d = {}
for c in (65, 97):
for i in range(26):
d[chr(i+c)] = chr((i+13) % 26 + c)
print "".join([d.get(c, c) for c in s])
看到这里,你会会心一笑,这就是程序员的无聊和可爱之处,也是一种隐藏在geek范(作者是在向凯撒密码致敬么?)下的人文关怀。
我喜欢python除了喜欢代码的写法,另外一个原因是喜欢读python的代码。其实我们工作的大部分时间在读别人的代码。读python代码总是比读其它代码舒服些。除了格式统一外(很高兴go也在这方面通过gofmt做了努力),python的文化某种程度上保证了其代码的可读性。
python的人文关怀还体现在无处不在的dir
和help
上。在python shell(建议安装ipython)里,只要有了这两个武器,再加上一些必要的练习,你就能很快掌握一个新的库。还有什么比对初学者友好更友好的事情呢?
python可以应用在很多场合:
基本上这些领域都有很棒的python库供你驱使。
python的语言能力中规中矩,表现力稍弱于ruby。但它还是涵盖了从面向对象,函数式编程(有限支持)到元编程(有限支持)的主流思想和方法。由于昨天的文章中已经有几个python的例子来说明这一点,这里就不再重复。作为一个成功的语言,python并未固步自封,它一直在进行有益的进化。比如说2.5新加的with关键字,简化了try..finally结构,让代码更简洁漂亮(这一直是Python努力的方向):
with open('hello.txt') as f:
f.write('Hello world!\n')
这等价于之前的写法:
f = open('hello.txt')
try:
f.write('Hello world!\n')
finally:
f.close()
更关键的是,只需实现几个magic function,这种语言能力便能为你所用:
class MyOpen:
def __init__(self, filename, mode = 'r'):
self.filename = filename
self.mode = mode
def __enter__(self):
self.f = open(self.filename, self.mode)
return self.f
def __exit__(self, **unused):
self.f.close()
with MyOpen('hello.txt') as f:
f.write('hello world!\n')
现代编程语言的竞争是语言能力的竞争,也是语言的库和工具的竞争。对python而言,标准库为你提供了各种基本的能力,社区里繁多的第三方库更是将这种能力推到了一个新的高度。比如说github上著名的"kennethreitz/requests"库,它让http client的撰写简直就像写文章一样简单直观:
In [5]: import requests
In [6]: r = requests.get('https://api.github.com', auth=('user', 'pass'))
In [7]: r.status_code
Out[7]: 200
In [9]: r.json()
Out[9]:
{...}
In [10]: r.text
Out[10]: u'{"..."}'
利用这样的类库,加上github api,你可以十几二十行代码就撰写一个代码全文搜索工具。这就是web时代的生产力。
再比如scrapy(一个crawler framework),你只需定义好抓取的规则,抓好的数据怎么存储,它就能并发地帮你抓取并格式化数据。最有节操的是,它还提供了一个美妙的shell,让你在其中交互式地不断试错,直到可以正确定义好抓取的规则。
在2.3里python有了generator,它是coroutine的基石。generator允许你挂起当前的执行点,使得同步的代码转为异步,顺序执行的程序具备潜在并行执行(Parallelism)的能力,比如说我们做个fabonacci数列:
def fabonacci():
a, b = 1, 2
while True:
a, b = b, a+b
yield b
通过generator,代码一下子具备了lazy evaluation的能力,只有在你需要的时候,数据才被计算出来。也许你在这里看不到并发的影子,那么请你想象一下满是generator的世界,每个generator都有自己的执行栈,如果你写个scheduler,将generator调入调出,这不就是coroutine么?当然,python有自己的coroutine库,gevent,基于效率最高的libev。比如用gevent实现actor model(erlang的基石):
import gevent
from gevent.queue import Queue
class Actor(gevent.Greenlet):
def __init__(self):
self.inbox = Queue()
Greenlet.__init__(self)
def receive(self, message):
raise NotImplemented()
def _run(self):
self.running = True
while self.running:
message = self.inbox.get()
self.receive(message)
简单,明了。
好吧,任何语言总有其阴暗面。Python(CPython)有个臭名昭著的GIL(当然这也不是python独有的,ruby也有MRI),全局解释锁,任何代码的执行都必须先获得这个全局锁,当有IO操作时释放这把锁。有了这个全局锁,Python的threading实际上是一个虚假的概念,无论你有多少个thread,只能使用一个核。你可以做个threading的实验:
import threading
def dead_loop():
while True:
pass
# new dead loop thread
t = threading.Thread(target=dead_loop)
t.start()
# dead loop on main thread
dead_loop()
t.join()
以及multiprocessing的测试:
import multiprocessing
def dead_loop():
while True:
pass
# new dead loop process
p = multiprocessing.Process(target=dead_loop)
p.start()
# dead loop on main process
dead_loop()
p.join()
看看二者CPU占用率的差异。threading很不给力啊!
当然,你无须为此感到太悲观。多线程用不到Multicore的能力,但多进程可以,虽然多进程开销大些,但终究能多少弥补GIL带来的缺憾。