鱼C论坛

 找回密码
 立即注册
查看: 3973|回复: 10

[技术交流] 写给新人,python的代码风格

[复制链接]
发表于 2018-2-4 23:28:41 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 蓝色王魂 于 2018-2-5 08:39 编辑

        今天在和群里的小伙伴聊天的时候有了写这篇文章的想法,其实我学python也就不到三个月的时间,但我敢说我python面向对象的基础我都掌握了,其实的剩下的无非是自学和积累经验,后面我打算用pygame写一个类似橙光的文字游戏编辑器,不过这不是我们今天要讨论的,今天我们要说的是python的代码风格。
        我先说点次要的,有以下几点, 函数定义 类定义 模块导入之间都要空行, =号 ==号等两边打空格,逗号后面打空格,注释和注释的内容同一缩进,能做到这些很简单——用pycharm,这些都做到,能有效提高你代码的可读性。
        这一段还是讲次要的,就是变量的名称,我自己的方法是,都用对应的英语单词,忘了就用拼音,然和类的名字用名词,方法用动词,变量定义用名词并且注意单复数,有了这些,其实你就不需要写注释了。
        接下来就是我要说的重点了,众所周知,python是2017的top1,我认为这主要是有三点原因:1.简洁优美的代码风格 2.方便维护 3.有很多的第三方库,代码风格主要是关于第一点和第二点的,python是一个面向对象的语言,所以面向对象的编程方式是python的精髓,这个小甲鱼老师都有提到,所以,我的观点就是,写任何程序都要用类与继承!!
       下面举一个例子,就是小甲鱼老师的那个经典的猜数字游戏,先来看一下按当时所学的我认为的一个比较好的实现。
  1. import random

  2. print('这是一个猜数字的小游戏')
  3. secret = random.randint
  4. count = 0
  5. flag = 0

  6. while 1:
  7.     if count == 0:
  8.         guess = int(input('请输入你要猜的数字(1-9):'))
  9.     elif count < 3:
  10.         guess = int(input('猜错啦,请重新输入:'))
  11.     else:
  12.         break
  13.     if guess == secret:
  14.         flag = 1
  15.         break
  16.     else:
  17.         count += 1
  18. if flag == 0:
  19.     print('太菜了,猜三次都猜不出来')
  20. else:
  21.     print('你是小甲鱼肚子里的蛔虫吗')
复制代码

再看一下类与对象的方法:
  1. import random

  2. class Game():

  3.     def __init__(self):
  4.         self.secret = random.randint(1, 9)
  5.         self.count = 0
  6.         self.flag = 0

  7.     def guess(self):
  8.         if self.count == 0:
  9.             g = input('请输入你要猜的数字(1-9):')
  10.         elif self.count < 3:
  11.             g = input('猜错啦,请重新输入:')
  12.         if self.count == 3:
  13.             return 0
  14.         return int(g)

  15.     def judge(self):
  16.         while 1:
  17.             g = self.guess()
  18.             if self.count == 3:
  19.                 break
  20.             if g == self.secret:
  21.                 print('你是小甲鱼肚子里的蛔虫吗')
  22.                 self.flag = 13
  23.             else:
  24.                 self.count += 1

  25.     def run(self):
  26.         self.judge()
  27.         if self.flag == 1:
  28.             print('游戏结束')
  29.         else:
  30.             print('太菜了,三次都猜不出来')


  31. if __name__ == '__main__':
  32.     game = Game()
  33.     game.run()
复制代码


        对游戏编程有些了解的鱼油肯定知道这种写法就是游戏的一般写法,两段代码完全是一回事,明显第一个很简洁,但是,由于python自己的优美语法,我敢说后面的代码给一个没学过python的人看都能看懂,这就是代码优美的威力,而且写成这样的形式很方便给这个游戏进行功能添加,你只要多加一个方法就行了,这就是我说的易于维护。
        我下面再发一个代码,是一个神经网络算法的实现,具体是用来识别数字的,因为原来的作者用的是python2,所以我自己进行了修改,把原来的注释都删除了,但我们主要是看的代码风格,如果对神经网络感兴趣,请移步http://neuralnetworksanddeeplearning.com/chap1.html
  1. import random
  2. import numpy as np

  3. def sigmoid(a):
  4.     return 1.0 / (1.0 + np.exp(-a))

  5. def sigmoid_prime(z):
  6.     return sigmoid(z)*(1-sigmoid(z))

  7. class Network(object):

  8.     def __init__(self, sizes):
  9.         self.num_layers = len(sizes)
  10.         self.sizes = sizes
  11.         self.biases = [np.random.randn(y, 1) for y in sizes[1:]]
  12.         self.weights = [np.random.randn(y, x)\
  13.                         for x, y in zip(sizes[:-1], sizes[1:])]

  14.     def feedforward(self, a):
  15.         for b, w in zip(self.biases, self.weights):
  16.             a = sigmoid(np.dot(w, a)+b)
  17.         return a

  18.     def SGD(self, training_data, epochs, mini_batch_size, eta\
  19.             , test_data=None):
  20.         if test_data:
  21.             n_test = len(test_data)
  22.         n = len(training_data)
  23.         for j in range(epochs):
  24.            random.shuffle(training_data)
  25.            mini_batches = [training_data[k:k+mini_batch_size]\
  26.                            for k in range(0, n, mini_batch_size)]
  27.            for mini_batch in mini_batches:
  28.                self.update_mini_batch(mini_batch, eta)
  29.            if test_data:
  30.                print('Epoch {0}: {1} / {2}'.format(\
  31.                    j, self.evaluate(test_data), n_test))
  32.            else:
  33.                print('Epoch {0} complete'.format(j))

  34.     def update_mini_batch(self, mini_batch, eta):
  35.         nabla_b = [np.zeros(b.shape) for b in self.biases]
  36.         nabla_w = [np.zeros(w.shape) for w in self.weights]
  37.         for x, y in mini_batch:
  38.             delta_nabla_b, delta_nabla_w = self.backprop(x, y)
  39.             nabla_b = [nb+dnb for nb, dnb in zip(nabla_b, delta_nabla_b)]
  40.             nabla_w = [nw+dnw for nw, dnw in zip(nabla_w, delta_nabla_w)]
  41.         self.weights = [w-(eta/len(mini_batch))*nw for w, nw in zip(self.weights, nabla_w)]
  42.         self.biases = [b - (eta/len(mini_batch)) * nb for b, nb in zip(self.biases, nabla_b)]

  43.     def backprop(self, x, y):
  44.         nabla_b = [np.zeros(b.shape) for b in self.biases]
  45.         nabla_w = [np.zeros(w.shape) for w in self.weights]
  46.         activation = x
  47.         activations = [x]
  48.         zs = []
  49.         for b, w in zip(self.biases, self.weights):
  50.             z = np.dot(w, activation) + b
  51.             zs.append(z)
  52.             activation = sigmoid(z)
  53.             activations.append(activation)
  54.         delta = self.cost_derivative(activations[-1], y) * \
  55.             sigmoid_prime(zs[-1])
  56.         nabla_b[-1] = delta
  57.         nabla_w[-1] = np.dot(delta, activations[-2].T)
  58.         for l in range(2, self.num_layers):
  59.             z = zs[-l]
  60.             sp = sigmoid_prime(z)
  61.             delta = np.dot(self.weights[-l + 1].transpose(), delta) * sp
  62.             nabla_b[-l] = delta
  63.             nabla_w[-l] = np.dot(delta, activations[-l - 1].transpose())
  64.         return (nabla_b, nabla_w)

  65.     def evaluate(self, test_data):
  66.         test_results = [(np.argmax(self.feedforward(x)), y) for (x, y) in test_data]
  67.         return sum(int(x == y) for (x, y) in test_results)

  68.     def cost_derivative(self, output_activations, y):
  69.         return (output_activations-y)
复制代码

        你们自己体会这个代码的优美吧。
        做一个小小的总结,对于新人来说,好的代码风格就和好的投篮姿势一样,如果你一开始就能掌握,那就能事半功倍,我花了一个多月才发现类的优点,然后一用就不可自拔,假设你已经不是新手了,而想成为python大神,首先你需要代码量到三万(基础),其次你需要对某一方面有深入了解,第三,最好对计算机科学有深入研究,这是我的观点。
        最后再扯几句,我发现写文章开头不用按好几个空格,一个tab搞定,这是我学python得到的经验,还有最后就是感谢小甲鱼老师,他的教程在b站python搜索第一位是有理由的,没有他,我根本不会对编程有兴趣,@小甲鱼, I love fishc.另外,我想有没有人给我介绍一下python在北京的实习,本人还是高中生,想多积累点经验,也是为以后学java做铺垫,万分感谢,qq569908467,如果有实习联系我,想要交流python的也行(男生必须学完零基础,女生不限)

评分

参与人数 1荣誉 +1 鱼币 +1 贡献 +1 收起 理由
小白要成神 + 1 + 1 + 1 感谢楼主无私奉献!

查看全部评分

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2018-2-4 23:48:14 | 显示全部楼层
得了吧,你还是赶紧自己去搜一下google开源项目风格指南中的python代码规范以及PEP8的内容

这是业内已经成熟的规范,你不要自己去另起炉灶了

https://google.github.io/styleguide/pyguide.html
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-2-5 00:06:56 | 显示全部楼层
DarkmasterSugar 发表于 2018-2-4 23:48
得了吧,你还是赶紧自己去搜一下google开源项目风格指南中的python代码规范以及PEP8的内容

这是业内已经 ...

我这都是自己总结的,而且也观摩过大神的代码基本上是这种风格,但还是谢谢指正
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-2-5 00:21:15 | 显示全部楼层
@小甲鱼 求课后作业填坑
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-2-5 00:27:56 | 显示全部楼层
蓝色王魂 发表于 2018-2-5 00:06
我这都是自己总结的,而且也观摩过大神的代码基本上是这种风格,但还是谢谢指正

会总结当然是好事,不过我觉得你这样的帖容易误导部分新人
再者,大神的代码绝不会是和你贴出来的这样的,不规范的地方太多了,典型的高效学院风
比如单行长度有超过80字符的;
二元运算符两侧没有空格,且代码中前后不一致
采用\这样的很扎眼的换行方式
过长参数列表处理欠妥
类和函数都没有写文档等等

参考别人风格要有鉴别能力,有google这样的推荐规范就按推荐的来
你再看看random, numpy这样成熟的库的源代码是个什么风格,肯定不是长这样的
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-2-5 02:53:27 | 显示全部楼层
DarkmasterSugar 发表于 2018-2-5 00:27
会总结当然是好事,不过我觉得你这样的帖容易误导部分新人
再者,大神的代码绝不会是和你贴出来的这样的 ...

差不多就行了,我觉得不用要求每个人风格都一样,那个人是一个机器学习的工程师,这是他出的一个电子书的内容。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-2-5 02:54:30 | 显示全部楼层
本帖最后由 蓝色王魂 于 2018-2-5 03:21 编辑
DarkmasterSugar 发表于 2018-2-5 00:27
会总结当然是好事,不过我觉得你这样的帖容易误导部分新人
再者,大神的代码绝不会是和你贴出来的这样的 ...


还有,这代码是我改的,不是他的原版代码,所以会有细小问题是我自己疏忽了,那天用的idle编的不是pychram,我觉得有时候idle看起来舒服
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-2-5 02:58:33 | 显示全部楼层
蓝色王魂 发表于 2018-2-5 02:54
还有,这代码是我改的,不是他的原版代码,所以会有细小问题我懒得改,而且我说了,文档被我删了。

原版代码,python2的
  1. """
  2. network.py
  3. ~~~~~~~~~~

  4. A module to implement the stochastic gradient descent learning
  5. algorithm for a feedforward neural network.  Gradients are calculated
  6. using backpropagation.  Note that I have focused on making the code
  7. simple, easily readable, and easily modifiable.  It is not optimized,
  8. and omits many desirable features.
  9. """

  10. #### Libraries
  11. # Standard library
  12. import random

  13. # Third-party libraries
  14. import numpy as np

  15. class Network(object):

  16.     def __init__(self, sizes):
  17.         """The list ``sizes`` contains the number of neurons in the
  18.         respective layers of the network.  For example, if the list
  19.         was [2, 3, 1] then it would be a three-layer network, with the
  20.         first layer containing 2 neurons, the second layer 3 neurons,
  21.         and the third layer 1 neuron.  The biases and weights for the
  22.         network are initialized randomly, using a Gaussian
  23.         distribution with mean 0, and variance 1.  Note that the first
  24.         layer is assumed to be an input layer, and by convention we
  25.         won't set any biases for those neurons, since biases are only
  26.         ever used in computing the outputs from later layers."""
  27.         self.num_layers = len(sizes)
  28.         self.sizes = sizes
  29.         self.biases = [np.random.randn(y, 1) for y in sizes[1:]]
  30.         self.weights = [np.random.randn(y, x)
  31.                         for x, y in zip(sizes[:-1], sizes[1:])]

  32.     def feedforward(self, a):
  33.         """Return the output of the network if ``a`` is input."""
  34.         for b, w in zip(self.biases, self.weights):
  35.             a = sigmoid(np.dot(w, a)+b)
  36.         return a

  37.     def SGD(self, training_data, epochs, mini_batch_size, eta,
  38.             test_data=None):
  39.         """Train the neural network using mini-batch stochastic
  40.         gradient descent.  The ``training_data`` is a list of tuples
  41.         ``(x, y)`` representing the training inputs and the desired
  42.         outputs.  The other non-optional parameters are
  43.         self-explanatory.  If ``test_data`` is provided then the
  44.         network will be evaluated against the test data after each
  45.         epoch, and partial progress printed out.  This is useful for
  46.         tracking progress, but slows things down substantially."""
  47.         if test_data: n_test = len(test_data)
  48.         n = len(training_data)
  49.         for j in xrange(epochs):
  50.             random.shuffle(training_data)
  51.             mini_batches = [
  52.                 training_data[k:k+mini_batch_size]
  53.                 for k in xrange(0, n, mini_batch_size)]
  54.             for mini_batch in mini_batches:
  55.                 self.update_mini_batch(mini_batch, eta)
  56.             if test_data:
  57.                 print "Epoch {0}: {1} / {2}".format(
  58.                     j, self.evaluate(test_data), n_test)
  59.             else:
  60.                 print "Epoch {0} complete".format(j)

  61.     def update_mini_batch(self, mini_batch, eta):
  62.         """Update the network's weights and biases by applying
  63.         gradient descent using backpropagation to a single mini batch.
  64.         The ``mini_batch`` is a list of tuples ``(x, y)``, and ``eta``
  65.         is the learning rate."""
  66.         nabla_b = [np.zeros(b.shape) for b in self.biases]
  67.         nabla_w = [np.zeros(w.shape) for w in self.weights]
  68.         for x, y in mini_batch:
  69.             delta_nabla_b, delta_nabla_w = self.backprop(x, y)
  70.             nabla_b = [nb+dnb for nb, dnb in zip(nabla_b, delta_nabla_b)]
  71.             nabla_w = [nw+dnw for nw, dnw in zip(nabla_w, delta_nabla_w)]
  72.         self.weights = [w-(eta/len(mini_batch))*nw
  73.                         for w, nw in zip(self.weights, nabla_w)]
  74.         self.biases = [b-(eta/len(mini_batch))*nb
  75.                        for b, nb in zip(self.biases, nabla_b)]

  76.     def backprop(self, x, y):
  77.         """Return a tuple ``(nabla_b, nabla_w)`` representing the
  78.         gradient for the cost function C_x.  ``nabla_b`` and
  79.         ``nabla_w`` are layer-by-layer lists of numpy arrays, similar
  80.         to ``self.biases`` and ``self.weights``."""
  81.         nabla_b = [np.zeros(b.shape) for b in self.biases]
  82.         nabla_w = [np.zeros(w.shape) for w in self.weights]
  83.         # feedforward
  84.         activation = x
  85.         activations = [x] # list to store all the activations, layer by layer
  86.         zs = [] # list to store all the z vectors, layer by layer
  87.         for b, w in zip(self.biases, self.weights):
  88.             z = np.dot(w, activation)+b
  89.             zs.append(z)
  90.             activation = sigmoid(z)
  91.             activations.append(activation)
  92.         # backward pass
  93.         delta = self.cost_derivative(activations[-1], y) * \
  94.             sigmoid_prime(zs[-1])
  95.         nabla_b[-1] = delta
  96.         nabla_w[-1] = np.dot(delta, activations[-2].transpose())
  97.         # Note that the variable l in the loop below is used a little
  98.         # differently to the notation in Chapter 2 of the book.  Here,
  99.         # l = 1 means the last layer of neurons, l = 2 is the
  100.         # second-last layer, and so on.  It's a renumbering of the
  101.         # scheme in the book, used here to take advantage of the fact
  102.         # that Python can use negative indices in lists.
  103.         for l in xrange(2, self.num_layers):
  104.             z = zs[-l]
  105.             sp = sigmoid_prime(z)
  106.             delta = np.dot(self.weights[-l+1].transpose(), delta) * sp
  107.             nabla_b[-l] = delta
  108.             nabla_w[-l] = np.dot(delta, activations[-l-1].transpose())
  109.         return (nabla_b, nabla_w)

  110.     def evaluate(self, test_data):
  111.         """Return the number of test inputs for which the neural
  112.         network outputs the correct result. Note that the neural
  113.         network's output is assumed to be the index of whichever
  114.         neuron in the final layer has the highest activation."""
  115.         test_results = [(np.argmax(self.feedforward(x)), y)
  116.                         for (x, y) in test_data]
  117.         return sum(int(x == y) for (x, y) in test_results)

  118.     def cost_derivative(self, output_activations, y):
  119.         """Return the vector of partial derivatives \partial C_x /
  120.         \partial a for the output activations."""
  121.         return (output_activations-y)

  122. #### Miscellaneous functions
  123. def sigmoid(z):
  124.     """The sigmoid function."""
  125.     return 1.0/(1.0+np.exp(-z))

  126. def sigmoid_prime(z):
  127.     """Derivative of the sigmoid function."""
  128.     return sigmoid(z)*(1-sigmoid(z))
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-2-5 02:59:30 | 显示全部楼层
蓝色王魂 发表于 2018-2-5 02:58
原版代码,python2的

我想吐槽他的换行有加反斜杠有没加的。。。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-2-5 09:55:32 From FishC Mobile | 显示全部楼层
现在还在学学到30课就觉得后面越来越复杂了,不知道类是怎么一回事。感谢楼主分享
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-3-13 16:54:02 | 显示全部楼层
讲道理,我觉得这里不值得用类来实现。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-5-14 22:07

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表