Python之禅

保持简单、追求简单,我想这就是编码之中的禅,一种回归本真的境界。这种禅意在 Python 的设计哲学中体现的淋漓尽致,在 Python 解释器中输入“import this”,便会出现经典的 Python 之禅。

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 way not be obvious at first unless you’re Dutch.
虽然一开始这种方法并不是显而易见的,但谁叫你不是Python之父呢。

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!
命名空间是个绝妙的想法,让我们多多地使用它们吧!

参考

用Python实现编程练习Kata-FizzBuzzWhizz

Functional programming leads to deep insights into the nature of computation. – Martin Odersky

近日软件匠艺小组的朋友用函数式编程的方式重新实现了一个古老的Kata练习题:FizzBuzzWhizz,用了很多种语言及其特性,非常精妙。这是一个软件匠艺的文艺复兴时代。
原帖见https://codingstyle.cn/topics/100

题目

FizzBuzzWhizz详细描述请自行查阅相关资料。此处以3, 5, 7为例,形式化地描述一下问题。

r1
+ times(3) -> Fizz
+ times(5) -> Buzz
+ times(7) -> Whizz
r2
+ times(3) && times(5) && times(7) -> FizzBuzzWhizz
+ times(3) && times(5) -> FizzBuzz
+ times(3) && times(7) -> FizzWhizz
+ times(5) && times(7) -> BuzzWhizz
r3
+ contains(3) -> Fizz
+ the priority of contains(3) is highest
rd
+ others -> others

Python实现

借助原帖的测试用例,我用python语言也练习了一把。本来想引入Pipe库用管道式的方式写,可能会取得一些方便,但想想还是用原生的语法来做吧。

继续阅读 More

函数式Python中的Pipe与itertools

可迭代器(iterable),不仅限于list/str等,还包括任何包含有yield关键字的函数,后者未必有规律的迭代特征。标准库中的itertools包提供了更加灵活的产生迭代器的工具,这些工具的输入大都是已有的迭代器函数的封装,并且itertools给出的函数都是针对广义迭代器而言。而len()等函数是针对狭义迭代器,即sequence(i.e. str, list, tuple)而言的。
以内置函数range()为例,执行结果会是一次性计算好整个序列。这对于很长的序列来说会比较耗时,甚至带来性能问题。因而,python还提供了内置函数,提供了惰性求值版本,那就是xrange()。它利用yield特性,第一次执行时仅仅返回迭代器,不到用时是不会求值的。
实际上,itertools提供的函数都是惰性的,并且给原内置函数都重写了惰性版本。如imap()对于内置的map()

扩展库Pipe则对内置函数和部分itertools进行了封装,提供了类似unix bash下的管道式调用风格,更接近人类从左到右的阅读习惯,使得代码更加优雅。其他动态语言,如ruby, c#-lambda java8-lambda也都提供了类似的链式调用形式。
另外,也提供了@Pipe装饰器,可以非常方便地扩展出自己的管道函数,或者继续封装其他itertools中的有用函数。

这里是Pipe官方给出的例子,用管道函数式编程解出https://projecteuler.net/中的三道题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# Find the sum of all the multiples of 3 or 5 below 1000.
euler1 = (itertools.count() | select(lambda x: x * 3) | take_while(lambda x: x < 1000) | add) \
+ (itertools.count() | select(lambda x: x * 5) | take_while(lambda x: x < 1000) | add) \
- (itertools.count() | select(lambda x: x * 15) | take_while(lambda x: x < 1000) | add)
assert euler1 == 233168

# Find the sum of all the even-valued terms in Fibonacci which do not exceed four million.
euler2 = fib() | where(lambda x: x % 2 == 0) | take_while(lambda x: x < 4000000) | add
assert euler2 == 4613732

# Find the difference between the sum of the squares of the first one hundred natural numbers and the square of the sum.
square = lambda x: x * x
euler6 = square(itertools.count(1) | take(100) | add) - (itertools.count(1) | take(100) | select(square) | add)
assert euler6 == 25164150

注意:所有惰性求值的迭代器,都是只能求值一次的,如果再次求值会什么也得不到,因为yield堆栈已经走到底,无法回头。因此,当要对惰性迭代器重复使用时,必须故意地提前将其求值展开,或者利用itertools.tee来克隆一个迭代器。

继续阅读 More

Python中多继承与super()用法

Python类分为两种,一种叫经典类,一种叫新式类。两种都支持多继承。

考虑一种情形,B继承于A,C继承于A和B, 但C需要调用父类的init()函数时,前者会导致父类A的init()函数被调用2次,这是不希望看到的。而且子类要显式地指定父类,不符合DRY原则。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 经典类
class A():
def __init__(self):
print 'A'

class B(A):
def __init__(self):
A.__init__(self)
print 'B'

class C(B, A):
def __init__(self):
A.__init__(self)
B.__init__(self)
print 'C'

采用新式类,要求最顶层的父类一定要继承于object,这样就可以利用super()函数来调用父类的init()等函数,每个父类都执行且执行一次,并不会出现重复调用的情况。而且在子类的实现中,不用到处写出所有的父类名字,符合DRY原则。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 新式类
class A(object):
def __init__(self):
print 'A'

class B(A):
def __init__(self):
super(B, self).__init__()
print 'B'

class C(B, A):
def __init__(self):
super(C, self).__init__()
print 'C'

采用super()方式时,会自动找到第一个多继承中的第一个父类,但是如果还想强制调用其他父类的init()函数或两个父类的同名函数时,就要用老办法了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

class Person(object):
name = ""
sex = ""
def __init__(self, name, sex='U'):
print 'Person'
self.name=name
self.sex=sex


class Consumer(object):
def __init__(self):
print 'Consumer'

class Student(Person, Consumer):
def __init__(self, score,name):
print Student.__bases__
super(Student, self).__init__(name, sex='F')
Consumer.__init__(self)
self.score=score

s1 = Student(90, 'abc')
print s1.name, s1.score, s1.sex

理解unicode和utf8字符编码

本文以python语言为例,解答下面几个问题:理解unicode的统一性,unicode与utf8和ASCII到底是什么关系?又如何在实践中用好字符编解码呢?

令人头疼的字符编解码乱象

由于历史原因,中文字符编码标准众多,特别是在Windows操作系统上,GBK,GB2312,UTF8等等,各不相同,还记得各种场合出现的乱码吗?而在Mac系统上,由于完全统一用UTF8编码,所以编程人员在开发调试时就不大会因为编码操心。然而,一旦涉及到与第三方系统对接,还是不免会遇到编解码的问题,不论这种多系统集成的模式叫做SOA或者MicroService也好,每个系统都有自己的接口定义,即使是用SOAP协议夜还会有不同版本,更不用说各种自定义的基于XML或JSON的准Restful协议了。
总之,不同操作系统,不同的第三方系统,带来了不同字符编解码复杂局面。

继续阅读 More

骑士周游-马踏棋盘问题(A Knight's Journey)

看到 @大城小胖 做了个H5游戏:马踏棋盘,于是想起研究一下这个算法。

题目是这样的:国际象棋的棋盘为8*8的方格棋盘,将“马”放在任意指定的方格中,按照“马”走棋的规则将“马”进行移动。要求每个方格只能进入一次,最终使得“马”走遍棋盘64个方格。(N=8的情况)

我用python实现了这个算法,其中用到了回溯法,并用贪心法进行优化,以防递归深度太深而溢出。当找到一个解就停止递归。

不过这个解的最后一步与初始位置难以重合,而@大城小胖 的游戏中却要求马最后要回到初始位置,因此算法还要进化

继续阅读 More

Python函数式编程

函数式编程

如果程序中的函数仅接受输入并产生输出,即输出只依赖于输入,内部数据不可变,避免保存程序状态,用同样的输入值反复调用可以得到相同的结果,那么这种编程范式就称为函数式编程(Functional Programming,简称FP,又称泛函编程)

这种风格也称声明式编程(Declarative Programming),与之相对的是指令式编程(Imperative Programming),后者中的对象会不断修改自身状态。函数式编程强调程序的执行结果比执行过程更重要,倡导利用若干简单的执行单元让计算结果不断渐进,逐层推导复杂的运算,而不是设计一个复杂的执行过程。

函数编程语言最重要的基础是λ演算(lambda calculus),函数可以像数值一样被赋值于变量,还可以作为其他函数的输入(引数)和输出(传出值)进行传递。

函数式编程历史悠久,最古老的例子莫过于1958年被创造出来的LISP了。而随着程序结构复杂,面向对象编程大行其道。近年来,简洁而且特别适合计算任务的函数式编程又重新崛起,不仅仅是纯粹的函数式语言如Haskell、Clojure、Elixir等,各种流行语言javascripts、python、Objective-C、C#、Swift甚至Java都纷纷吸收函数式编程的部分形式。而且,不仅仅是计算任务,近年还出现了用FP编写的UI应用程序,如LightTable等。

Paul Graham在《黑客与画家》一书中写道:同样功能的程序,极端情况下,Lisp代码的长度可能是C代码的二十分之一。

本文作者@申导 主要采用Python语言为例,是因为它虽然不是纯粹的FP,但Python能够胜任各种编程形式,简洁优雅,通俗易懂,语法接近于Java/C++,特别适合从主流语言转过来的学习者。

继续阅读 More

在Windows上采用IIS来运行Django

给客户做一个内部软件项目,客户愿意配合敏捷、精益创业的思维,快速交付之后再不断通过用户反馈来调整。
于是就采用基于Python语言v2.7的Django框架V1.4来进行开发。
花了些日子,初具雏形。双方约定先部署,让内部员工开始试用。然后客户把目标服务器交给我了。

Django对Apache或Nginx的支持会方便些(http://www.jackyshen.com/2012/08/12/running-django-gunicorn-via-nginx/)。可是,这是一台带有公网IP的托管的Windows Server2003。上面运行有IIS 6.0,并且已经有另外两个web应用在运行。为了尽量能不影响原有应用,就考虑尝试一下在Windows上采用IIS来运行Django。

于是google一把,结果发现了PyISAPIe…

继续阅读 More