使用Python编程时的一些思考

前两天做数据挖掘的作业,是我第一次用Python写一个有点规模(两百多行)的程序,第一次真正使用Python的类和namedtuple,在编程的过程中对Python语言产生了不少思考,在这里先记录下来,以后有时间的时候就仔细探究一下。

1. Python的面向对象机制

Python类的语法有一个很鲜明的特征,就是每个方法的第一个参数都需要是self,类似于Java中的this引用。我在编程过程中发现,无论何时引用一个类内的成员或方法,都需要通过self来访问,即self.xxx。我有些怀疑,Python类的语法是否只是一个语法糖,解释器直接将方法调用转化为一个函数,而由于程序员已经在方法的第一个参数上写上了self,解释器都不需要再改变方法的参数列表了。而Python的类不支持访问控制(public/protected/private),似乎也印证了这一点。

2. 列表推导和生成器

列表推导(list comprehension)是Python里很棒的一个语法,也是我喜欢Python的原因之一。列表推导可以实现函数式编程中map, filter, apply的功能,而且更加清晰明了。关于列表推导,我很想一探背后的原理,列表推导是用for循环来实现的吗?列表推导会产生多少临时变量?了解这些之后,才能更好地使用这个功能。

列表推导和函数式编程一样都有一个缺点,它返回的是一个全新的列表,在数据量很大的时候,这会降低不少性能。所以在对很大的数据进行操作的时候我也不愿意使用列表推导。不过生成器(generator)是一个很好的解决方案,它不会一次生成所有的结果,只会在被迭代的时候每次产生一个结果,有种惰性求值的感觉。生成器的写法和列表推导一样,只需将方括号变为圆括号。在作为函数参数时,括号还可以省略,像:

1
sum(i * i for i in range(10))

值得一提的是,range函数在Python2中返回的是一个列表,而在Python3中返回的是一个生成器。如果你只进行一次迭代操作,生成器和列表没有什么区别。但在我的印象中,生成器似乎只能进行一次迭代,不能重复使用,除非再创建一个一样的生成器。我在编程中曾想使用生成器函数,产生一个生成器,传入本来需要一个列表的函数,但因为函数中这个列表需要被使用两遍,只得作罢。

3. 善用namedtuple

Python这样的语言当然没有struct,如果你想表示一个图结构,每个结点需要保存结点内容、入边和出边。可以使用有三个元素的列表:[item, prev, next]。但这样在访问的时候,就需要通过下标2来访问next,看起来一点都不直观。在编程的时候,每次要用到一个结点,都要思考一下列表里哪个位置代表哪个对象,这也是我在使用Python编程时效率较低的主要原因。

有没有什么办法能让我直接用node.next来访问呢?使用一个类我感觉是不太合适的,因为语法会比较复杂,而且开销也会比较大。后来我找到了namedtuple,以前提说了这个数据结构,但一直不明白它的用处,现在我终于有了一些理解。

1
Node = namedtuple('Node', ['item', 'prev', 'next'])

这样就定义了一个Node类型,它有三个元素,分别叫item、prev和next。在使用的时候,可以直接像类成员一样进行访问:node.next,也可以直接和创建类一样的语法进行初始化:Node(item, prev, next)。但是namedtuple也有一个问题,和tuple一样,成员是不可修改的,这就限制了使用场景。不过似乎namedtuple提供了方法进行更新操作,有待我进一步研究。

4. Python列表的引用

按照我对计算机系统的理解,以及以前学习Python时模糊的印象,Python的变量应该都是一个引用,指向堆空间中的一个对象。所以这些变量在作为参数进行传递的时候,传递的都是引用,不会出现把整个列表的值拷贝一遍的情况。以前我用Python写过一个贝叶斯分类器的代码,因为不太清楚语言运行时的规则,为了确保运行效率,不得已把一些变量作为全局变量。如果我能理解变量是一个引用,应该就能写出更好的代码。这也印证了理解计算机系统会显著提高编程水平。

5. Python链表实现

Python中没有指针,因此要实现基于指针的数据结构(树、图)需要一定的技巧。之前我看到过没有指针的语言有一个链表的游标实现。不过我没有翻阅相关资料,在编程中基于字典构造了图结构。一个nodes字典维护了结点id到结点对象的一个映射。每个结点对象是一个三元组(item, prev, next),其中item是结点内容,prev是结点入边邻居的id集合,next是结点出边邻居的id集合。找到id之后就可以从nodes中找到对应的结点。不知道有没有更好的实现方法,以及Python有没有关于这样数据结构的库。