Python 编程规范

Auth : 张旭
Date : 2018-04-02
Email: zhangxu@1000phone.com

  1. PEP8 编码规范, 以及开发中的一些惯例和建议

    • 代码编排:
      • 缩进 4 个空格, 禁止空格与 Tab 混用
      • 行长 80: 防止单行逻辑过于复杂
    • import
      • 不要使用 from xxx import *
      • 顺序
        1. 标准库
        2. 第三方库
        3. 自定义库
      • 单行不要 import 多个库
      • 模块内用不到的不要去 import
    • 空格
      • : , ; 后面跟一个空格, 前面无空格 (行尾分号后无空格)
      • 二元操作符前后各一个空格, 包括以下几类:
        1. 数学运算符: + - * / // = & |
        2. 比较运算符: == != > < >= <= is not in
        3. 逻辑运算符: and or not
        4. 位运算符: & | ^ << >>
      • = 用于指示关键字参数或默认参数值时, 不要在其两侧使用空格
    • 适当添加空行
      • 函数间: 顶级函数间空 2 行, 类的方法之间空 1 行
      • 函数内: 同一函数内的逻辑块之间, 空 1 行
      • 文件结尾: 留一个空行 (Unix 中 \n 是文件的结束符)
    • 注释
      • : 逐行添加注释,没有一个注释
      • 行内注释: 单行逻辑过于复杂时添加
      • 块注释: 一段逻辑开始时添加
      • 引入外来算法或者配置时须在注释中添加源连接, 标明出处
      • 函数和类尽可能添加 docstring
    • 命名
      • 除非在 lambda 函数中, 否则不要用 单字母 的变量名 (即使是 lambda 函数中的变量名也应该尽可能的有意义)
      • 包名、模块名、函数名、方法名全部使用小写, 单词间用下划线连接
      • 类名、异常名使用 CapWords (首字母大写) 的方式, 异常名结尾加 ErrorWraning 后缀
      • 全局变量尽量使用大写, 一组同类型的全局变量要加上统一前缀, 单词用下划线连接
    • 字符串拼接尽量使用 join 方式: 速度快, 内存消耗小
    • 语意明确、直白
      • not xx in yy VS xx not in yy
      • not a is b VS a is not b
    • 程序的构建
      • 一个函数只做一件事情, 并把这件事做好
      • 大的功能用小函数之间灵活组合来完成
      • 避免编写庞大的程序, “大” 意味着体积庞大, 逻辑复杂甚至混乱
    • 函数名必须有动词, 最好是 do_something 的句式, 或者 somebody_do_something 句式
    • 自定义的变量名、函数名不要与标准库中的名字冲突
    • pip install pep8 pylint flake8
    • 练习: 规范化这段代码

      1. from django.conf import settings
      2. import sys, os
      3. mod=0xffffffff
      4. def foo (a , b =123 ):
      5. c= { 'x' : 111 , 'y' : 222}#定义一个字典
      6. d =[1, 3 ,5 ]
      7. return a ,b, c
      8. def bar(x):
      9. if x%2== 0: return True
  1. *** 的用法

    • 函数定义

      1. def foo(*args, **kwargs):
      2. pass
    • 参数传递

      1. def foo(x, y, z, a, b):
      2. print(x)
      3. print(y)
      4. print(z)
      5. print(a)
      6. print(b)
      7. lst = [1, 2, 3]
      8. dic = {'a': 22, 'b': 77}
      9. foo(*lst, **dic)
    • import * 语法

      1. from xyz import *
      2. __all__ = ('a', 'e', '_d')
      3. a = 123
      4. b = 456
      5. c = 'asdfghjkl'
      6. _d = [1,2,3,4,5,6]
      7. e = (9,8,7,6,5,4)
    • 强制命名参数

      1. def foo(a, *, b, c=123):
      2. pass
    • 解包语法: a, b, *ignored, c = [1, 2, 3, 4, 5, 6, 7]

  1. Python 的赋值和引用

    • ==, is: == 判断的是值, is 判断的是内存地址 (即对象的id)
    • 小整数对象: [-5, 256]
    • copy, deepcopy 的区别

      • copy: 只拷贝表层元素
      • deepcopy: 在内存中重新创建所有子元素
      • copy and deepcopy
    • 练习1: 说出执行结果

      1. def extendList(val, lst=[]):
      2. lst.append(val)
      3. return lst
      4. list1 = extendList(10)
      5. list2 = extendList(123, [])
      6. list3 = extendList('a')
    • 练习2: 说出下面执行结果

      1. from copy import copy, deepcopy
      2. from pickle import dumps, loads
      3. a = ['x', 'y', 'z']
      4. b = [a] * 3
      5. c = copy(b)
      6. d = deepcopy(b)
      7. e = loads(dumps(b, 4))
      8. b[1].append(999)
      9. c[1].append(999)
      10. d[1].append(999)
      11. e[1].append(999)
    • 自定义 deepcopy: my_deepcopy = lambda item: loads(dumps(item, 4))

  1. 迭代器, 生成器

    1. class Range:
    2. def __init__(self, start, end, step):
    3. self.start = start - step
    4. self.end = end
    5. self.step = step
    6. def __iter__(self):
    7. return self
    8. def __next__(self):
    9. current = self.start + self.step
    10. if current < self.end:
    11. self.start = current
    12. return current
    13. else:
    14. raise StopIteration()
    • iterator: 任何实现了 __iter____next__ (python2中是 next()) 方法的对象都是迭代器.

      • __iter__返回迭代器自身
      • __next__ 返回容器中的下一个值
      • 如果容器中没有更多元素, 则抛出StopIteration异常
    • generator: 生成器其实是一种特殊的迭代器, 不需要自定义 __iter____next__

      • 生成器函数 (yield)
      • 生成器表达式
    • 练习1: 定义一个随机数迭代器, 随机范围为 [1, 50], 最大迭代次数 30

      1. import random
      2. class RandomIter:
      3. def __init__(self, start, end, times):
      4. self.start = start
      5. self.end = end
      6. self.max_times = times
      7. self.count = 0
      8. def __iter__(self):
      9. return self
      10. def __next__(self):
      11. self.count += 1
      12. if self.count <= self.max_times:
      13. return random.randint(self.start, self.end)
      14. else:
      15. raise StopIteration()
    • 练习2: 自定义一个迭代器, 实现斐波那契数列

      1. class Fib:
      2. def __init__(self, max_value):
      3. self.prev = 0
      4. self.curr = 1
      5. self.max_value = max_value
      6. def __iter__(self):
      7. return self
      8. def __next__(self):
      9. if self.curr < self.max_value:
      10. res = self.curr
      11. self.prev, self.curr = self.curr, self.prev + self.curr
      12. return res
      13. else:
      14. raise StopIteration()
    • 练习3: 自定义一个生成器函数, 实现斐波那契数列

      1. def fib(max_value):
      2. prev = 0
      3. curr = 1
      4. while curr < max_value:
      5. yield curr
      6. prev, curr = curr, curr + prev
    • 迭代器、生成器有什么好处?

      • 节省内存
      • 惰性求值
    • itertools

      • 无限迭代
        • count(start=0, step=1)
        • cycle(iterable)
        • repeat(object [,times])
      • 有限迭代
        • chain(*iterables)
      • 排列组合
        • product(*iterables, repeat=1) 笛卡尔积
        • permutations(iterable[, r-length]) 全排列
        • combinations(iterable, r-length) 组合
    • 各种推导式

      • 列表: [i for i in range(5)]
      • 字典: {i: i + 3 for i in range(5)}
      • 集合: {i for i in range(5)}
  2. 装饰器

    • 最简装饰器

      1. def deco(func):
      2. def wrap(*args, **kwargs):
      3. return func(*args, **kwargs)
      4. return wrap
      5. @deco
      6. def foo(a, b):
      7. return a ** b
    • 原理

      • 对比被装饰前后的 foo.__name__foo.__doc__

        1. from functools import wraps
        2. def deco(func):
        3. '''i am deco'''
        4. @wraps(func)
        5. def wrap(*args, **kwargs):
        6. '''i am wrap'''
        7. return func(*args, **kwargs)
        8. return wrap
      • 简单过程

        1. fn = deco(func)
        2. foo = fn
        3. foo(*args, **kwargs)
      • 多个装饰器调用过程

        1. @deco1
        2. @deco2
        3. @deco3
        4. def foo(x, y):
        5. return x ** y
        6. # 过程拆解 1
        7. fn3 = deco3(foo)
        8. fn2 = deco2(fn3)
        9. fn1 = deco1(fn2)
        10. foo = fn1
        11. foo(3, 4)
        12. # 过程拆解 2
        13. deco1(
        14. deco2(
        15. deco3(foo)
        16. )
        17. )(3, 4)
    • 带参数的装饰器

      1. def deco(n):
      2. def wrap1(func):
      3. def wrap2(*args, **kwargs):
      4. return func(*args, **kwargs)
      5. return wrap2
      6. return wrap1
    • 装饰器类和 __call__

      1. class Deco:
      2. def __init__(self, func):
      3. self.func = func
      4. def __call__(self, *args, **kwargs):
      5. return self.func(*args, **kwargs)
      6. @Deco
      7. def foo(x, y):
      8. return x ** y
      9. # 过程拆解
      10. fn = Deco(foo)
      11. foo = fn
      12. foo(12, 34)
    • 使用场景

      • 参数、结果检查
      • 缓存、计数
      • 日志、统计
      • 权限管理
      • 重试
      • 其他
    • 练习1: 写一个 timer 装饰器, 计算出被装饰函数调用一次花多长时间, 并把时间打印出来

      1. import time
      2. from functools import wraps
      3. def timer(func):
      4. @wraps(func) # 修正 docstring
      5. def wrap(*args, **kwargs):
      6. time0 = time.time()
      7. result = func(*args, **kwargs)
      8. time1 = time.time()
      9. print(time1 - time0)
      10. return result
      11. return wrap
    • 练习2: 写一个权限管理装饰器, 权限分为 admin / member / guest 三级

    • 练习3: 写一个 Retry 装饰器

      1. import time
      2. class retry(object):
      3. def __init__(self, max_retries=3, wait=0, exceptions=(Exception,)):
      4. self.max_retries = max_retries
      5. self.exceptions = exceptions
      6. self.wait = wait
      7. def __call__(self, f):
      8. def wrapper(*args, **kwargs):
      9. for i in range(self.max_retries + 1):
      10. try:
      11. result = f(*args, **kwargs)
      12. except self.exceptions:
      13. time.sleep(self.wait)
      14. continue
      15. else:
      16. return result
      17. return wrapper
  1. method, classmethodstaticmethod

    • method: 通过实例调用时, 可以引用类内部的任何属性和方法

    • classmethod: 无需实例化, 可以调用类属性和类方法, 无法取到普通的成员属性和方法

    • staticmethod: 无论用类调用还是用实例调用, 都无法取到类内部的属性和方法, 完全独立的一个方法

    • 练习: 说出下面代码的运行结果

      1. class Test(object):
      2. x = 123
      3. def __init__(self):
      4. self.y = 456
      5. def bar1(self):
      6. print('i am a method')
      7. @classmethod
      8. def bar2(cls):
      9. print('i am a classmethod')
      10. @staticmethod
      11. def bar3():
      12. print('i am a staticmethod')
      13. def foo1(self):
      14. print(self.x)
      15. print(self.y)
      16. self.bar1()
      17. self.bar2()
      18. self.bar3()
      19. @classmethod
      20. def foo2(cls):
      21. print(cls.x)
      22. # print(cls.y)
      23. # cls.bar1()
      24. Test.bar2()
      25. Test.bar3()
      26. @staticmethod
      27. def foo3(obj):
      28. print(obj.x)
      29. print(obj.y)
      30. obj.bar1()
      31. obj.bar2()
      32. obj.bar3()
      33. t = Test()
      34. t.foo1()
      35. t.foo2()
      36. t.foo3()
  1. Python 魔术方法

    1. __str__, __repr__
    2. __init____new__

      • __new__ 返回一个对象的实例, __init__ 无返回值
      • __new__ 是一个类方法

        • 单例模式

          1. class A(object):
          2. '''单例模式'''
          3. obj = None
          4. def __new__(cls, *args, **kwargs):
          5. if cls.obj is None:
          6. cls.obj = object.__new__(cls)
          7. return cls.obj
    3. 比较运算、数学运算

      • 运算符重载

        • +: __add__(value)
        • -: __sub__(value)
        • *: __mul__(value)
        • /: __truediv__(value) (Python 3.x), __div__(value) (Python 2.x)
        • //: __floordiv__(value)
        • %: __mod__(value)
        • &: __and__(value)
        • |: __or__(value)
      • 练习: 实现字典的 __add__ 方法, 作用相当于 d.update(other)

        1. class Dict(dict):
        2. def __add__(self, other):
        3. if isinstance(other, dict):
        4. new_dict = {}
        5. new_dict.update(self)
        6. new_dict.update(other)
        7. return new_dict
        8. else:
        9. raise TypeError('not a dict')
      • 比较运算符的重载

        • ==: __eq__(value)
        • !=: __ne__(value)
        • >: __gt__(value)
        • >=: __ge__(value)
        • <: __lt__(value)
        • <=: __le__(value)
      • 练习: 完成一个类, 实现数学上无穷大的概念

        1. class Inf:
        2. def __lt__(self, other):
        3. return False
        4. def __le__(self, other):
        5. return False
        6. def __ge__(self, other):
        7. return True
        8. def __gt__(self, other):
        9. return True
        10. def __eq__(self, other):
        11. return False
        12. def __ne__(self, other):
        13. return True
    4. 容器方法

      • __len__ -> len
      • __iter__ -> for
      • __contains__ -> in
      • __getitem__string, list, tuple, dict 有效
      • __setitem__list, dict 有效
      • __missing__dict 有效, 字典的预留接口, dict 自身并没有实现

        1. class Dict(dict):
        2. def __missing__(self, key):
        3. self[key] = None # 当检查到 Key 缺失时, 可以做任何默认行为
    5. 可执行对象: __call__

    6. with:
      • __enter__ 进入 with 代码块前的准备操作
      • __exit__ 退出时的善后操作
    7. __setattr__, __getattribute__, __getattr__, __dict__

      • 常用来做属性监听

        1. class A:
        2. '''TestClass'''
        3. z = [7,8,9]
        4. def __init__(self):
        5. self.x = 123
        6. self.y = 'abc'
        7. def __setattr__(self, name, value):
        8. print('set %s to %s' % (name, value))
        9. object.__setattr__(self, name, value)
        10. def __getattribute__(self, name):
        11. print('get %s' % name)
        12. return object.__getattribute__(self, name)
        13. def __getattr__(self, name):
        14. print('not has %s' % name)
        15. return -1
        16. def foo(self, x, y):
        17. return x ** y
        18. # 对比
        19. a = A()
        20. print(A.__dict__)
        21. print(a.__dict__)
    8. 槽: __slots__

      • 固定类所具有的属性
      • 实例不会分配 __dict__
      • 实例无法动态添加属性
      • 优化内存分配

        1. class A:
        2. __slots__ = ('x', 'y')
  1. 闭包

    • 说出下面函数返回值

      1. def foo():
      2. l = []
      3. def bar(i):
      4. l.append(i)
      5. return l
      6. return bar
      7. f1 = foo()
      8. f2 = foo()
      9. # 说出下列语句执行结果
      10. f1(1)
      11. f1(2)
      12. f2(3)
    • 作用域

      • global
      • nonlocal
      • globals()
      • locals()
      • vars()

        1. local namespace
        2. |
        3. V
        4. global namespace
        5. |
        6. V
        7. builtin namespace
    • 更深入一点: __closure__

  1. Python 性能之困

    1. 计算密集型
      • CPU 长时间满负荷运行, 如图像处理、大数据运算、圆周率计算等
      • 计算密集型: 用 C 语言补充
      • Profile, timeit
    2. I/O 密集型: 网络 IO, 文件 IO, 设备 IO 等
      • 多任务处理: 进程 / 线程 / 协程
      • 阻塞 -> 非阻塞
      • 同步 -> 异步
    3. GIL 全局解释器锁
      • 它确保任何时候都只有一个Python线程执行。
      • GIL
    4. 什么是进程、线程、协程?
      • 进程: 资源消耗大, 系统整体开销大, 数据通信不方便
      • 线程: 资源消耗小, 可共享数据。上下文开销大。按时间片强制切换, 不够灵活
      • 协程: 内存开销更小, 上下文切换开销更小。可根据事件切换, 更加有效的利用 CPU
      • 进程、线程、协程调度的过程叫做上下文切换
    5. 什么是同步、异步、阻塞、非阻塞?
      • 同步, 异步: 客户端调用服务器接口时
      • 阻塞, 非阻塞: 服务端发生等待
    6. 事件驱动 + 多路复用
      • 轮询: select, poll
      • 事件驱动: epoll 有效轮询
    7. greenlet / gevent | tornado / asyncio

      1. import asyncio
      2. async def foo(n):
      3. for i in range(10):
      4. print('wait %s s' % n)
      5. await asyncio.sleep(n)
      6. return i
      7. task1 = foo(1)
      8. task2 = foo(1.5)
      9. tasks = [asyncio.ensure_future(task1),
      10. asyncio.ensure_future(task2)]
      11. loop = asyncio.get_event_loop()
      12. loop.run_until_complete(asyncio.wait(tasks))
    8. 线程安全, 锁

      • 获得锁之后, 一定要释放, 避免死锁
      • 获得锁之后, 执行的语句, 只跟被锁资源有关
      • 区分普通锁 Lock, 可重入锁 RLock
      • 线程之间的数据交互尽量使用 Queue
    9. gevent
      • monkey.patch
      • gevent.sleep 非阻塞式等待
      • Queue 协程间数据交互, 避免竞争
  1. Garbage Collection (GC)

    • 引用计数

      • 优点: 简单、实时性高
      • GC
      • 缺点: 消耗资源、循环引用

        1. lst1 = [3, 4] # lst1->ref_count 1
        2. lst2 = [8, 9] # lst2->ref_count 1
        3. # lst1 -> [3, 4, lst2]
        4. lst1.append(lst2) # lst2->ref_count 2
        5. # lst2 -> [8, 9, lst1]
        6. lst2.append(lst1) # lst1->ref_count 2
        7. del lst1 # lst1->ref_count 1
        8. del lst2 # lst2->ref_count 1

        ref_loop

    • 标记-清除, 分代收集

  1. 继承、多继承、多态、Mixin、super

    • 继承
    • 多态

      1. class Animal:
      2. pass
      3. class Cat(Animal):
      4. pass
      5. class Tom(Cat):
      6. pass
      7. tom = Tom()
      8. isinstance(tom, Cat)
      9. isinstance(tom, Animal)
    • 多继承

      • Cls.mro()
      • 菱形继承问题

        1. # 继承关系示意
        2. #
        3. # A.foo()
        4. # / \
        5. # B C.foo()
        6. # \ /
        7. # D
        8. class A:
        9. def foo(self):
        10. print('I am A')
        11. class B(A):
        12. pass
        13. class C(A):
        14. def foo(self):
        15. print('I am C')
        16. class D(B, C):
        17. pass
        18. d = D()
        19. d.foo()
        20. print(D.mro())
    • Mixin

    • super

      1. class A:
      2. def __init__(self):
      3. print('enter A')
      4. self.x = 111
      5. print('exit A')
  1. class B(A):
  2. def __init__(self):
  3. print('enter B')
  4. A.__init__(self)
  5. # super().__init__()
  6. print('exit B')
  7. class C(A):
  8. def __init__(self):
  9. print('enter C')
  10. A.__init__(self)
  11. # super().__init__()
  12. print('exit C')
  13. class D(B, C):
  14. def __init__(self):
  15. print('enter D')
  16. B.__init__(self)
  17. C.__init__(self)
  18. # super().__init__()
  19. print('exit D')
  20. d = D()
  1. 一些技巧和误区

    1. 格式化打印
      • json.dumps(data, indent=4, sort_keys=True, ensure_ascii=False)
      • json 压缩: json.dumps(obj, separators=[',',':'])
      • pprint
    2. 确保能取到有效值
      • d.get(k, default)
      • d.setdefault
      • defaultdict
      • a or b
      • x = a if foo() else b
    3. try…except… 的滥用
      • 不要把所有东西全都包住, 程序错误需要报出来
      • 使用 try...except 要指明具体错误, try 结构不是用来隐藏错误的, 而是用来有方向的处理错误的
    4. 利用 dict 做模式匹配

      1. def do1():
      2. print('i am do1')
      3. def do2():
      4. print('i am do2')
      5. def do3():
      6. print('i am do3')
      7. def do4():
      8. print('i am do4')
      9. mapping = {1: do1, 2: do2, 3: do3, 4: do4}
      10. mod = random.randint(1, 10)
      11. func = mapping.get(mod, do4)
      12. func()
    5. inf, -inf, nan

    6. venv, pyenv, 命名空间
      • venv: 创建虚拟环境, 做环境隔离, venv 目录直接放到项目的目录里
      • pyenv: 管理 Python 版本
    7. property: 把一个方法属性化

      1. class C(object):
      2. @property
      3. def x(self):
      4. "I am the 'x' property."
      5. return self._x
      6. @x.setter
      7. def x(self, value):
      8. self._x = value
      9. @x.deleter
      10. def x(self):
      11. del self._x
    8. else 子句: if, for, while, try

    9. collections 模块
      • defaultdict
      • OrderedDict
      • Counter
      • namedtuple

欢迎关注我的微信公众号互相交流

qrcode_for_gh_b8fe229d156f_258 (1).jpg