2014年,我国发布了语言法,目的就是要规范我国语言的使用,方便大家的沟通、文化的传承。语言有语法,程序也有语法,但程序的语法除了要过解释器以外,剩下的就看程序员的习惯了,这里并没有什么硬性的规定,主要是一些约定俗成的做法。首先是一些官方的推荐写法,能查到的东西我也不多说,具体可以参照Python编码规范。如果你觉得里面的内容太难记,一时半会不能背下来,那我告诉你,千万别背!这里要推荐一下我用的IDE——PyCharm,相信大家一定都听说过,其强大的功能,除了一些编程有的提示以为,那就是把编码规范的检查给原生加进去了,包括命名、空格、缩进等等,如果是有强迫症的朋友,把滚动条上方的绿色方框改到变成绿色无波浪线的勾勾,那就说明你的代码基本符合了规范。
但依然有很多规范是无法被检测的,例如命名。鉴于大家平日生活的语言环境都是中文,所以命名也就千差万别,好的是使用英文缩写,但缩写的位数不定,更有甚着,使用拼音,最不能理解的,是拼音的缩写!还是拼音的首字母缩写!!!还没有注释!!!!!虽然很多人说,好的工程师是写文档,又有人说,更好的工程师是写注释,但从本科里,我的导师就跟我说,一段好的代码,是不需要注释和文档的,“it speaks itself!”所以,我强烈推荐大家使用英文的全单词,不要怕长,如果为了解释一个两个字母的变量名你花了一段话,那样更臃肿。英文全单词构成的变量和方法,再加上Python本来已经非常接近自然语言的保留字,你会发现一段好的代码就像一篇好的文章,即便是不懂编程的人,也能看懂。如果你觉得你的英文水平不行,不要怕麻烦,就算每新建一个变量,百度一遍又如何?长期下来,对提高阅读英文文档的能力肯定有一定帮助。
Python之所以被推崇,其中一个原因就是它的工具包很多,所以当你要做一件事情的时候,首先去看看有没有工具包支持,但import一个module以后,别忙着用,也别忙着拷贝网上的代码,Python是开源的,你可以先看文档,不行直接看源码,好的文档或者源码都有官方提供的例子,工具包的编写者写出这样的类或者方法,肯定有他的期望的用途,按照这些官方的例子,你可以直接理解到作者的意图,用最合适的方式去使用这个工具包。这里举个例子:
* Restart training from a saved graph and checkpoints.
* Run inference from a saved graph and checkpoints.
```Python
...
# Create a saver.
saver = tf.train.Saver(...variables...)
# Remember the training_op we want to run by adding it to a collection.
tf.add_to_collection('train_op', train_op)
sess = tf.Session()
for step in xrange(1000000):
sess.run(train_op)
if step % 1000 == 0:
# Saves checkpoint, which by default also exports a meta_graph
# named 'my-model-global_step.meta'.
saver.save(sess, 'my-model', global_step=step)
```
Later we can continue training from this saved `meta_graph` without building
the model from scratch.
```Python
with tf.Session() as sess:
new_saver = tf.train.import_meta_graph('my-save-dir/my-model-10000.meta')
new_saver.restore(sess, 'my-save-dir/my-model-10000')
# tf.get_collection() returns a list. In this example we only want the
# first one.
train_op = tf.get_collection('train_op')[0]
for step in xrange(1000000):
sess.run(train_op)
```
在官方的源代码里面,关于saver,有这样一个例子,可以说,在无意中看到这个例子之前,我在网上看到的代码基本没提过collection,就算有,也只是草草带过,大部分的代码也只是直接使用本地变量存储Tensor的引用,而没有使用到collection,这使得代码的全局性欠缺,并没有很好地领悟到作者写这个功能的意图。除了collection,例子中还提到了图的输出和重载,这样,我们并不需要每次都新建一系列的Tensor,当代码训练完后,我们在使用Checkpoint的时候直接恢复图表就可以了,大大提高了代码的迁移和重用性。除了Tensorflow,logging和thread模块也有很多曲解作者意图的代码在网络上广泛传播着,这里便不一一展开。
当我们理解了其他作者的抽象以后,便需要开始抽象组织自己的代码,我从来不认为第一次编码能够有多规范或者多抽象,我也没有在编码前过多设计类图的习惯,很多时候有了一个初步的想法,我就会写代码实现它。第一次的代码基本就是一篇流水账,然后我会看看代码中有什么可以重用的地方,便抽象出一个函数,当一系列的函数需要共用一些参数的时候或者有业务的单元经常需要复用或者批量新建的时候,便考虑新建一个类,然后放在另一个文件里,当很多文件都需要共同的变量的时候,就考虑把变量放在一个配置文件。可以说,我重构代码的时间是第一次实现功能的几倍,每隔一段时间我都会回头看自己的代码。我始终都记得本科导师的那句话,“过分的设计是万恶的根源”,因为你的能力、工具包的能力、计算机的能力、甚至业务的需求都是不断改变的,我们要的不是完备的设计,而是方便维护的代码,不断迭代。在不断重构中,你的设计能力也会不断增强,我总结出了一些原则,供大家参考:
1、使用工具包的时候,优先寻找工厂方法,能够使用工厂方法获取的实例,如果不是代码过长的话,没必要使用本地变量存储,直接使用;
2、“do one thing”,当你的函数,或者循环结构里面有太多行的时候,你就应该考虑新建一个函数来做这件事情,然后直接调用这个函数,而不是做一个臃肿的循环结构。
3、代码中不显式出现字符串,包括key和提示语,这点可以参照一下tensorflow.GraphKeys,里面把常用的key都用常量存起来。然后对于提示语,可以放在配置文件中,用变量和函数读取。这里特别提一点,对于带变量的提示语,建议使用“%d%s%f”这些格式符号,而不是把字符串和变量用“+”或者“,”连起来,以前一直不理解为何要这样使用,直到后来发现,当我们要把提示语放到配置文件的时候,使用格式符号,可以在同一条记录里面修改整条语句,保证语义完整,也方便做不同的语言包。
4、不要出现绝对路径,很多时候我们的代码都需要在win和Linux之间迁移,使用绝对路径的时候你会需要两份配置文件乃至两份代码,善用os.path这个工具。
5、只在一个类的方法中使用到的函数,即便不需要用到类属性,也建议用静态方法去实现,而不要放在类外面的结构里,因为类名也是对函数名的一种解释,而且也便于使用者查找。
6、不是类方法共用的参数、不需要持续保存的参数,没必要做成类属性,减少对类属性的依赖可以使得函数的复用性更高。
7、方法参数的顺序尽量跟其出现的顺序一致。
8、调用多个参数的方法的时候,尽量把参数名带上,这样可以避免因为顺序错误而引起出错,也方便维护的人看懂代码。
9、很多时候,你会发现勤快地输出日志,比你注释你的代码或者逐步调试有用得多。你可以考虑使用不同级别的日志来区分正式使用和debug。
目前想到的就是这些,我觉得,个人习惯比语法重要得多,而且很多东西不是一蹴而就的,不要怕麻烦,重构你的代码吧!