python 之禅 import this
dongweiming的博客
前言
我这个博客一直都是一些技术分享,show code的地方,我从来没有写过个人生活或者情感杂谈,当然我也从来没有谈论过我对什么东西的喜恶. 很多人喜欢喷XX语言,喜欢谈论XX和YY的优缺,甚至凑了一本不知所云的书…好吧,我觉得没有使用一门语言超过10年,没有对一个技术研究个5,6年, 不好意思说自己懂(天才除外).我也觉得我没有资格讨论什么,也许我有我的观点看法,但是我怀着怀疑的心态看自己,生怕自己理解错了. 下文纯属个人吐槽,也许没有指定路怎么走,只是希望提个醒…
使用python2年,可喜的是python被越来越多的人接受,甚至前端工程师…但是却有点烂大街的感觉:感觉出门不聊几句python都不好意思和人打招呼.但是你真的懂python嘛?
你会python真的不重要
python实在太好学习了,假如你会其它的语言,可能搞本书翻一翻,一周后就能写很高端的python程序,由于web框架的普及,你甚至可以让一个网站应用跑起来. 你会我也会,你有什么竞争力?
你知道python怎么用嘛?
在什么时候需要使用OOP?
在什么时候使用类装饰器?
你用过元类嘛?
在什么时候用静态方法什么时候使用类方法?
你了解那些管理属性? call , init , __new__都是在什么时候被触发?__getattr__和__getattribute__应用有什么不同?
你知道标准库里面的多少个模块?你能在需要的时候知道这个功能其实标准库里面已经实现了?
什么时候用回调?
什么时候用signal?假如你会django你知道django的signal是什么?你了解orm嘛?
asyncore,contextlib, functools, collections, heapq,itertools, SocketServer, weakref,operator(知道3个就算)这些你会几个?
python的多态是什么?
在什么场景可以尝试python的设计模式中的XX(能想到2个场景就算)?
在什么时候可以使用Mixin?
在什么时候可以使用python的闭包?
你曾经用过yield嘛?生成器和迭代器的区别和应用场景是什么?
在什么可以使用python的函数式编程?
__future__模块里面都有什么定义的用法?
提笔想了这上面16点我认为体现python的东西,假如你不能有效的回答上面1/4, 好吧不要和我说你原来是会python的,踏实下来..你的路还很长.假如你回答不超过一半,我提醒你-你只是刚入行而已(这是我的角度)
假如我是一个入职后的带新人的引导者
学好git… 呵呵
假如新人还不熟悉python,python_koans是和不错的入门选择
首先就是严格的代码规范,加上团队的文化以及风格.
我会给一个任务,比如一周内写个多线程的socket命令行聊天程序,支持群组,加好友,群聊,发送文件等功能,看新人能力而定
而后把项目一部分略棘手的工作教给他,注意这里是生产环境,在他完成任务的过程中会熟悉我们的上线/code review/代码风格等东西
我希望整个团队一起贡献一个基础的公共库,包含一些常用的功能,然后新人首先学习这些东西,以后就不需要浪费时间造轮子,但是可以修改完善公共库, 这个公共库可以在新服务器部署时候直接使用pypi或者ubuntuPPA安装进来
什么算是好的python代码?
假如你的代码没使用pep8检验过,你已经无敌了. 最差你也要使用autopep8格式化差劲的代码吧?如果你想对自己的代码质量有要求,我强烈建议你了解什么是pythonic:
doughellmann 的作者 an-introduction-to-the-zen-of-python
be-pythonic
代码易懂但是堆和代码难懂但是精炼的取舍
我想很多对代码有追求的人都发现一件事情?看见项目中存在大量,没有被重用的函数,似曾相似的方法甚至方法的名字… 我是极为见不得ugly或者华而不实的代码的人, 但是有个问题. 我封装的代码很不直观,难懂..原来的代码貌似极为好懂.每个人都有自己的理解吧.就象我的团队里面有人说django代码太难懂,因为它们有django项目组的文化… celery代码写的不好这样的安慰似的评论…但是我不这样认为,我还在读celery代码,我也承认里面是有作者风格的取名或者实现的方式,但是我学到了很多.
通过看开源代码,比如django,requests,flask. 我会发现和总结很多别人的用法,思考别人为啥这样用.比如项目代码目录结构,解决一些问题使用的方式. 还有一些pep8中没有提到的规范. 比如一些代码实用的风格,举个例子:
我们的代码引用其它模块是这样的:
from test import long_long_long_test1
from test import long_long_long_test2
form test import long_long_long_test3
省略20多行,很闹残吧? 有一种风格是
from test import long_long_long_test1,
long_long_long_test2,
long_long_long_test3
说一个技巧:当我不知道该用什么,去看很NB项目怎么用 结果django和requests是这样的风格
from test import (long_long_long_test1,
long_long_long_test2,
long_long_long_test3)
怎么样进步?不是闭门造车..先去看看别人是怎么用的..
比如我以前拼接一个文件路径都是这样:
'{0}/{1}'.fotmat(dir, filename)
其实人家本来有:
os.path.join(dir, filename)
很惭愧.然后花了半个小时,把以前我这样用的地方全改了
这是我说的重要的一点: 知道了什么是对的 就要改….
对于这个主题我的答案: 我喜欢难懂但是精炼的代码. 境界就是你看的懂就能写的出来. 假如你连这点代码都看不懂.你看不了开源项目的做法.你会一直是个堆代码的码农..你会一直在堆着垃圾的代码.你会增加未来接手人的维护成本
怎么样提高python可读性和质量
以下是我的想法
首先给函数/类/方法取个好懂的名字(我这点很失败,英语太烂…是不是应该加一个学好英语)
当一个差不多的操作出现了三次,不要继续堆代码,要抽象出来
我倾向于写FIXME,TODO, 写文件/函数的用途的注释,在不是很好理解的代码上面注释作用,标明输入和输出都是什么(如果不是要修改维护你的代码,没人在乎你的算法多NB)
上面说的,请不要让别人需要仔细研究你的代码才明白是什么意思.. 我写代码很有压力,因为我不想接受我带嘛的人骂我.
不要炫技,请不要乱用函数式编程/闭包.我在乎的是性能和简单粗暴的实现功能
多用标准库的实现,如果不知道有这个功能实现前先google.
多读有名的项目,github上面有很多.思考别人为什么这样用
….
我们是封装开源项目还是直接修改开源代码给自己用
其实我这样描述,比如有个项目因为历史原因是一个很早的版本.但是和其它新的版本组件有兼容问题以及我们业务的特殊需要.我看了源码发现需要改动几个地方. 问题改动后就需要自己维护这个项目,对于新部署的环境甚至其它版本我还继续需要这个变动. 还有一种声音是”你不能修改XX源码”,你要在上面封装出一个新的东西, 也就是不直接调用XX,而是在我的自己的项目对XX有了个封装YY,然后我们的调用YY.
我觉得这个东西自己部署是一个可行的方案,首先这个修改不是一个patch,不是主流的修改.只能算是我们业务的二次开发而已,封装只是在掩耳盗铃. 着让我想起一个问题:为什么中国鲜有好的开源项目:中国人不缺好的idea?是因为中国人觉得这件事情做不了,是因为它们觉得别人实现的就是很牛比的, 自己改了就会有问题…其实这是自卑..首先是代码就会有bug,tornado/flask/requests不还是在开发和解决问题嘛?bug一直在只是你没有发现和注意. 我觉得开源项目的代码看懂了,了解了就可以修改..没什么可担心的…我指的是角度.我觉得每个人学了一门语言看了某个项目的源码只要你有胆量, 你有一个怀疑的善于发现和思考的心,那么你都能贡献你的代码,做你的二次开发.
和本文相关: 如果你没有做过这件事,你怎么可以说你会python?
我的感想
我不赞同”做好工作就好了”的调调.对你个人来说你明年今天做的事情和现在是一样的,不同的是你老了一岁. 如果是为什么完成工作而完成工作. 其实你这个代码就是线上运行的代码,并且是以后很长时间再用的版本,你随意的一些代码会在很久之后很难的变动.. 我也不同意一上来就把你的程序写的能承受千万级PV的架构.我认为对于现在项目状态,我要思考大约未来一年可能的发展,它如何简单的扩展就好了..
前言
对我来说,以前每次面试是我审视自己,检验自己的一种方式。每次准备面试,以及被面试官问住的时候才会发现,其实我python我学的还不够好。工作中也是,可以从其他的同事那里获得成长。但是我今天说的是,我也在自己总结和思考最佳实践这件事。
我想很多人都会有意识的去读一些PEP(Python Enhancement Proposals)。了解语言设计者当时的考虑,这些文案也是经过很长时间的讨论最后才实施的。既然想用好这门语言,必然需要理解设计之美。比如我听说gvanrossum使用emacs作为编辑器,我也使用emacs,就是希望我可以更贴近一些python。
本文根据 The Best of the Best Practices (BOBP) Guide for Python 和 Khan’s style-guides中对于开发中一些事物的理解和看法,有出至PEP,也有一些python界知名开发者,我加入了一些我自己的理解和看法。
价值观
“Build tools for others that you want to be built for you.” – Kenneth Reitz (Requests等知名库作者)
你自己都不想用的东西做出来有什么意义呢?
“Simplicity is alway better than functionality.” – Pieter Hintjens (ZeroMQ)
我对函数式编程的看法一直是看场景,甚至于我经常会对比性能,义无反顾的使用性能最好的,但是代码又不难懂和繁琐的
“Fit the 90% use-case. Ignore the nay sayers.” – Kenneth Reitz
程序员都有完美主义情怀,但是其实往往我们是在偏激的看事情 – 用户其实不case
“Beautiful is better than ugly.” – PEP 20
开发参考
“Explicit is better than implicit” – PEP 20
不要留坑,我经常看到一些复杂的代码,这些代码的作者写的时候明显知道自己在做什么,但是别人很难维护和看懂.
所以我对自己的职业的基本要求就是: 那天我离职了,后来接手的人不会经常骂我
“Readability counts.” – PEP 20
“Anybody can fix anything.” – Khan’s style-guides
我现在更多不是代码炫技,我经常思考的怎么让最少的代码,最简单的设计结构满足当前需求,也能给未来一段时间里也有扩展性
Fix each broken window (bad design,wrong decision,or poor code) as soon as it is discovered.
我们改bug有个原则 – 测试要覆盖到出bug的地方。每个人内心都有很高的代码质量的要求
“Now is better than never.” – PEP 20
明日复明日,明日何其多。我们在代码review的时候,问题需要在提出的时候就去改,永远不会说下一次再说,因为下一次大多时候是没有下一次了
Test ruthlessly. Write docs for new features.
Even more important that Test-Driven Development–Human-Driven Development
一些细节
PEP8
很多人是排斥的,假如你想让未来部门有自己的风格,习惯。让新人马上上手接受,PEP8是一个非常明智的选择
文件开头
新的文件的开头需要加一些docstring。描述文件的作用,编辑者,修改原因和日期等帮助阅读者的描述.
不要添加#!/usr/bin/python(除非这个文件未来是一个可执行的文件),copyright,__author__或者其他内容.
第一行建议添加# coding-utf-8
命名
Variables,functions,methods,packages,moduleslower_case_with_underscores
Classes and ExceptionsCapWords
Protected methods and internal functions_single_leading_underscore(self,…)
Private methods__double_leading_underscore(self,…)
ConstantsALL_CAPS_WITH_UNDERSCORES
Avoid one-letter variables (esp. l,O,I).永远不要使用没有意义的单字符作为变量名
PS: 这点可以折中,假如一个代码块代码逻辑很清晰,而 这个短的便令也只是过程中的一个间接变量之类的情况下是可以接受的
Good or Bad
列举一些正确和错误的用法.
Avoid redundant labeling.
复制代码
Good
import audio
core=audio.Core()
controller=audio.Controller()
Bad
import audio
core=audio.AudioCore()
controller=audio.AudioController()
复制代码
不要使用重复意义的标签
Prefer “reverse notation”.
复制代码
Good
elements=...
elements_active=...
elements_defunct=...
Bad
elements=...
active_elements=...
defunct_elements...
复制代码
Avoid getter and setter methods.
Good
person.age=42
Bad
person.set_age(42)
Indentation
永远不要Tab和空格混用。使用4个空格作为python缩进
Imports
Import entire modules instead of individual symbols within a module.
PS: 这个时候可以参考tornado的代码用法.
比如现在有这样一个包
$tree
└──canteen
├──__init__.py
├──sessions.py
复制代码
Good
import canteen
import canteen.sessions
from canteen import sessions
Bad
from canteen import get_user # Symbol from canteen/init.py
from canteen.sessions import get_session # Symbol from canteen/sessions.py
复制代码
PS: 除非这个第三方模块的文档显式的要求这些写
Splitting tricky lines
复制代码
Bad:
badge_name=badges.topic_exercise_badges.TopicExerciseBadge.name_for_topic_key_name(self.key().name())
Good:
badge_name=(badges.topic_exercise_badges.TopicExerciseBadge
.name_for_topic_key_name(self.key().name()))
Bad:
self.redirect("/class_profile?selected_graph_type=%s
复制代码
我添加的规则
from … import …
复制代码
Bad
from aa import alonglonglonglonglong,alonglonglonglonglonglonglonglonglong,
alonglonglonglonglonglong
Good
from aa import(alonglonglonglonglong,alonglonglonglonglonglonglonglonglong,
alonglonglonglonglonglong)
from aa import(alonglonglonglonglong,alonglonglonglonglonglonglonglonglong,
alonglonglonglonglonglong,alonglonglonglonglonglonglong,
alonglonglonglonglonglong2) # Good。 当引入的函数/类/变量很多时,也可以选择空 4 个空格的方式,而不需要和首行的左括号后对齐
复制代码
相对引用(relative import) 和 绝对引用(absolute import)
复制代码
$cat xx/models/user/consts.py # 如果想引用这个变量
TMP=1
$cat xx/views/user.py
from xx.models.user.consts import TMP # recommended。
假如模块层级>=3。 比如 xx/models/user/consts.py 就在根目录下的第 3 级。 如果其他当前目录下源文件需要调用它的内容。 也可以选择相对引用
$cat xx/models/user/main.py # 需要和 consts.py 在一个目录下才可以
from consts import TMP # Bad
from.consts import TMP # Good
而且只能在包内相对引用。 包外都需要绝对引用
复制代码
我Python 最佳实践指南
quoniammm
quoniammm
3 人赞了该文章
阅读这份指南我所学到的
- 代码风格
pip install pycodestyle
pycodestyle .py
pip install autopep8
autopep8 --in-place optparse.py
不包含 --in-place 标志将会使得程序直接将更改的代码输出到控制台,以供审查。
--aggressive 标志则会执行更多实质性的变化,而且可以多次使用以达到更佳的效果
my_very_big_string = (
"For a long time I used to go to bed early. Sometimes, "
"when I had put out my candle, my eyes would close so quickly "
"that I had not even time to say “I’m going to sleep.”"
)
行的延续
- Python Basics
嵌套函数与闭包
文件读取与写入 "a" "w" "r"
yield lambda 函数(即一种匿名函数)
面向对象编程 (属性与方法)
init(self, *arg) # 构造函数
str(self) #@override return str_name
static attribute # 定义在 init 函数外 所有类实例共享
继承与多态
class son(Parent):
pass
# super().method_name # 调用父类方法
注释
Pypi
调试 (vscode 大法好)
创建可执行文件 (pyinstaller)
setup 文件 requirements 文件
ansible???
python fg (终端)
/ or //
int float NoneType bool
str 在 python 里是 immutable sequences of unicode codepoints
bytes 在 python 里是 immutable sequences of bytes
.encode() .decode()
list 在 python 里是 mutable sequences of objects
dict 在 python 里是 mutable mappings of keys to values
Special attributes in Python are delimited by double underscores
"""
name gives us the means for our module to detect whether it has been run
as a script or imported into another module or the REPL.
"""
print(name)
if name == 'main':
function()
sys.argv[1]
advanced command line argument parsing:
Python Standard Library: argparse
Many third-party options such as docopt
docstrings 的使用方式
"""description
Args:
description
可选
Raises:
IOError: ......
Returns:
description
"""
#! 符号
!/use/bin/env python3
注意: Module code is executed exactly once, on first import 即上面的 print 函数只会执行一次
value equality vs. identity (使用 is)
一个容易出错的地方
a = [1, 2]
def replace(f):
f = [4, 5]
replace(a) # 结果: a 的值还是 [1, 2] 这其实还是一种闭包 (closure)
即 Function arguments are passed by object-reference (使用 id() 可以验证)
dir() 查看属性 name doc
#################################################
# Static # Dynamic
#################################################
Strong # # Python
#################################################
Weak # # JavaScript
#################################################
解释上图:
1.In a dynamic type system object types are only resolved at runtime
2.In a strong type system there is no implicit(隐式)type conversion
作用域
Local Enclosing Global Built-in(LEGB)
一个容易出错的地方
count = 0
def show_count():
print("count = ", count)
def set_count(c):
global count # 不加这一行会出错
count = c
Everything is an Object(一切皆对象)
we follow patterns Not to kill complexity But to master it
Special cases aren't special enough to break the rules
Python Collections(str/list/dict/tuple/range/set)
1.tuple
heterogeneous(各种各样的) immutable sequence
("www", 123, True) # in or not in
2. string
homogeneous(同类的,同性质的) immutable sequence of Unicode codepoints(characters)
.split() .join()
.partition() # divide a string into three around a seperator:
prefix, separator, suffix
3. range
arithmetic(算数,算法) progression of integers
4. List
heterogeneous mutable sequence
.index(item) # ValueError if not found
.insert(index, item)
.extend(list)
.reverse()
.sort() # reverse=True
built-in function
sorted(list)
list(reversed(list))
5. Dict
{}
dict([(), ()])
dicr(a=b, c=d)
.copy()
.update()
.values() # 忽略 keys
.keys() # 同上
.items() # 同上
from pprint import pprint as pp # pretty print 美化作用
6. Set
unordered collection of unique, immutable objects
{1, 4, 67, 8999}
s = set([list])
.remove()
.discard()
.copy()
.union() # 并集
.intersection() # 交集
.difference() # 交集的补集
.symmetric_difference() # 补集
.issubset() # 子集
.issuperset() # 父集
.isdisjoint() # 不相交
Handle Exception (觉得用不到哎 大部分时候)
Rasie/Handle/Unhandled exceptions/Exception objects
try:
except ValueError: # except (ValueError, TypeError) as e:
# str(e)
(finally)
return
Indentation Error/ SyntaxError/ NameError
Iterable Objects(可迭代对象)
List/Set/Dict Comprehension(递推式构造列表)
iter() # 获得 iterator
next() # 获得当前元素
generator 通关 yeild 生成
from itertools import islice, count
any()
zip()
unittest(单元测试)
unittest
debugging
pdb
distribute your program
in the face of ambiguity, refuse the temptation to guess(这句话很有道理)
You can pass -m to your Python command to have it run a module as a script
浅拷贝
- Python 进阶
3.1 Python 包(Package)与模块(Module)
简单理解包和模块的区别是:包是一组模块的集合
sys.path.append
export PYTHONPATH=path_name
包的结构
reader/init.py
reader/reader.py
touch reader/init.py
import reader
print(type(reader))
reader.file # 结果: './reader/init.py'
子包(Subpackages)
reader/compressed/init.py
reader/compressed/bzipped.py
reader/compressed/gzipped.py
import reader
import reader.compressed
import reader.compressed.bzipped
absoulte imports 和 relative imports
.b (当前路径下的module)
..a (上一路径下的module)
from ..a import A
from . import common
命名空间包
namespace packages (packages split across several directories)
useful for spliting large packages into multiple parts
namesapce packages have no init.py
This avoids complex initialization ordering problems
sys.path.extend(['path1', 'path2'])
main.py 可执行的文件夹 包 在终端
layout
project_name/
project_name/main.py
project_name/project_name/init.py
project_name/project_name/subpackage/init.py
project_name/project_name/test/init.py
project_name/setup.py
3.2 深入理解 Python 函数
call() # 一个 built_in 的函数 理念是 class 可以像 function 一样被执行
即 Callable instances
简洁表达
result = true_value if condition else false_value
lambda 函数
callable() # bulit_in 函数 检测是否可调用
*args **agrs (参数的映射)
extended() (对应展开)
t = (11, 12, 13, 14)
f(*t)
f(arg1, arg2, *arg3)
trace(f, *args, **args)
int('ff', base=16) # <=>
trace(int, "ff", base=16)
transpose tables
tansposed = list(zip(*daily)) # 神奇 cool
local function(区域函数 decorator closure)
def sort_by_last_letter(strings):
x = 'clo'
def last_letter(s): # 每次的区域函数并不相同,没有 cahe
return s[-1] + x
return sorted(strings, key=last_letter)
sort_by_last_letter.last_letter(s)
test = sort_by_last_letter('ert')
test.__closure__l
function factory : function that returns new, specialized functions(闭包的应用)
def raise_to(exp):
def raise_to_exp(x):
return pow(x, exp)
return rasie_to_exp
LEGB does not apply when making new bindings.
global: introduce names from global namespace into the local namespace
how can you do the same for name bindings in enclosing scopes?(所以有了 nonlocal)
nonlocal: introduce names from the enclosing namespace into the local namespace
cool~ 继续加油
decorator(装饰器): modify or enhance fucntions without changing their definition
implemented as callables that take and return other callables
replace, enhance, or modify existing functions
def escape_unicode(f):
def wrap(args, **kwargs):
x = f(args, **kwargs)
return ascii(x)
return wrap
@escape_unicode
def china_city():
return '西安'
用作装饰器的对象必须是 callable
class objects as decorators
class My_class:
def init(self, f):
self.f = f
self.count = 0
def call(self, args, **kwargs):
self.count += 1
return self.f(args, **kwargs)
@My_class
def hello(name):
print('Hello, {}'.format(name))
hello.count
class instances as decorators
class Trace:
def init(self):
self.enabled = True
def call(self, f):
def wrap(args, **kwargs):
if self.enabled:
print('Calling {}'.format(f))
return f(args, **kwargs)
return wrap
tracer = Trace()
@tracer
def rolate_list(l):
return l[1:] + [l[0]]
多个装饰器
@tracer
@escape_unicode
def china_island_maker(name):
return name + '岛'
class 里使用装饰器
class IslandMaker:
def init(self, suffix):
self.suffix = suffix
@tracer
def make_island(self, name)
return name + self.suffix
之前使用装饰器存在的问题: Naive decorators can lose important metadata
import functools
@functools.wraps()
例子: Validating Arguments
def check_non_negative(index):
def validator(f):
def wrap(args):
if args[index] < 0:
raise ValueError(
'Argument {} must be non-negative.'.format(index))
)
return f(args)
return wrap
return validator
@check_non_negative(1)
def create_list(value, size):
return [value] * size
3.3 深入理解 Python 面向对象编程
class attr(better not) vs. instance attr
static methods with the @staticmethod decorator ()
class methods with the @classmethod decorator (cls)
static methods 的继承
class methods 的继承
没什么特别之处 与普通的 methods 继承方法一样 (同样可以 override)
encapsulation(包装,包裹,封装) using the @property decorator
第一次接触以上概念 道理都懂 什么时候用啊 可以看懂程序就好 吧?!
@property
getter 函数
def attr1(self):
return self._attr1
@attr1.setter
setter 函数
def attr1(self, value):
self._attr1 = value
知乎的编辑器难用的更翔一样
狗币知乎 自动保存十分的恶心
Method Resolution Order(mro)
class-bound
instance-bound
(这部分内容有遗失,抽空补上)
多继承
class subClass(Base1, Base2, ...):
pass
method resolution order(MRO) determines name lookup in all cases
className.bases
className.mro (.mro()) # 见下图
C3 algorithm for calculating MRO in python
super() 感觉这个蛮重要的
super() returns a proxy object which routes method calls
Bound proxy: bound to a specific class or instance (更主要)
Unbound proxy: not bound to a class or instance
代理绑定分为: instance-bound and class-bound
下面 介绍 instance-bound proxy
super(ClassName, self).init() <=> super().__init()
即 look for a method on the base class
这也是一种实例绑定
calling super without arguments
3.4 深入理解 Python 语言特性
Exceptions and Errors
Exceptions are arranged in an inheritance hierarchy # assert
assert 5 > 2, "This is a error"
raise("some words")
except ValueError as e:
print("payload:", e.args)
chaining exception
traceback
Defining Context Managers
an object designed to be used in a with-statement
A context-manager ensures that resources are properly and automatically managed
Python introspection(自省)
很牛逼的属性 基本用不到 为什么用不到 因为你还不够牛逼
3.5 单元测试(Python 测试)
测试很重要 应该了解一下
unittest pytest doctest
python3 -m unittest -v
setup teardown
pytest
比 unittest 使用方便不少
就是导入你想要 test 的类
然后定义函数 以及一堆 assert 表达式
with pytest.raises(KeyError):
className.method(*arg)
pytest.skip("")
test fixture (setup 和 teardown)
@pytest.fixture
def phonebook(request):
phonebook = Phonebook()
def cleanup_phonebook():
phonebook.clear()
request.add_finalizer(cleanup_phonebook)
return phonebook
doctest
test double (目测暂时用不到 跳过)
the coverage and parameterized test(参数化测试)
1.measuring coverage of tests
2.using code coverage metrics when adding test cases
using a custom assert to reduce duplication
pip install coverage
pip install pytest-cov
python3 -m pytest --cov-report term-missing --cov tennis
python3 -m pytest --cov-report html --cov tennis
- Python 应用 (关于 Flask)
microframework
templates: Jinja 2 / Http and routing: Werkzeug / (Model), View, Controller
Blueprints
Development server + debugger
Unit testing support
- 总结