UT
我们一般用python的unittest写单元测试。调用的时候这么写:
if __name__ == '__main__':
unittest.main()
但这种写法要特别注意,不能使用from XXX import *语句,否则会把本文件引入的其它py文件里的UT都跑完!
在有from XXX import *语句的情况下,如果我只想跑本文件的UT,可以这么写:
def run_test():
suite = unittest.TestSuite()
loader = unittest.TestLoader()
# MyTest是本py文件里的UT类
suite.addTests(loader.loadTestsFromTestCase(MyTest))
runner = unittest.TextTestRunner(verbosity=2)
runner.run(suite)
if __name__ == "__main__":
run_test()
如果想执行python包下所有文件里的UT,可以这么写:
if __name__ == "__main__":
# 默认pattern是test*.py,这里我们改成*.py,因为我们的UT跟业务代码是写在一个文件里的
testsuite = unittest.TestLoader().discover('.', pattern='*.py')
unittest.TextTestRunner(verbosity=2).run(testsuite)
多重继承下构造函数的写法
假设类C同时继承A和B,必须在C的构造函数里同时调用A和B的构造器才行,但super()方法只返回第一个基类,因此需使用unbind构造器额外调用第二个基类的构造函数。样例如下:
class C(A, B):
def __init__(self, name, type, annotations):
# super() is A, using binding ctor, self NOT needed
super().__init__(annotations)
# call second base ctor,using unbind ctor, self needed
B.__init__(self, type)
self.name = name
动态执行能力
在spring里我们需要SpEL支持动态表达式,在python里就不用这么麻烦了,调用eval或exec即可。例如:
eval("'KEY' in ROOT or DYN_OPT.get('cfg_enabled', False)",
{'ROOT': '', 'DYN_OPT': {'cfg_enabled': True}})
参数1是要执行的py代码片段(这里是一个bool表达式),参数2是globals(供读取),参数3是locals(供写入),我们要的是eval的返回结果,故未使用参数3。
exec与eval不同之处在于,exec没有返回值。所以exec里要拿到上述结果,得用个变量V承接bool表达式的值,再到locals里去拿V,会麻烦一些。
反射
getattr就是python最基本的反射能力体现。
不过,可以使用inspect包,里面提供了很多有用的函数用于反射操作。
参数类型
python的参数分为几种类型:
位置参数,参数严格按定义的顺序来;
关键字参数,参数以名称来指定,不一定要按定义的顺序来
可变参数,包含一个星号和两个星号的参数。一个星号打头的参数,收集任意的位置参数,存放在一个tuple中;两个星号的参数,收集任意的关键字参数,放在一个dict中。使用的时候,要用单星号或双星号对tuple或dict做解包。
raw字符串
raw字符串是在字符串前加r,表示忽视字符串中的转义字符,反斜杠就当做反斜杠来处理。举个例子:
s = "abc\td"
# 打印abc d
print(s)
s = r"""abc\t\nd"""
# 打印abc\t\nd
print(s)
亦即,加了r前缀,反斜杠就是反斜杠,而不再是转义字符的前缀。
需要说明的是,转义字符必定以反斜杠做前缀,但并非前面有反斜杠的,就是转义字符。比如\d,无论怎么写,它都是反斜杠加上d的两个字符组成:
s=r"\d"
print(s)
print(len(s))
s="\d"
print(s)
print(len(s))
s="\\d"
print(s)
print(len(s))
上述代码中,打印出来的len(s)始终为2。因为反斜杠没法与d结合成为一个转义字符。
一些常见的转义字符有:
\n
\t
\r
\b
\f
\"
\'
import语句
这样的写法:
from .file1 import cls1
.file1表示从当前文件的同一个包的file1文件里导入cls1.
escape模式下的分割字符串
正常情况下,用split即可分割字符串。但考虑csv的情况:
csv的每一列用逗号分隔,但如果某列的内容里有逗号,则会将该列用引号括起来,进入escape模式,escape模式下的逗号是普通字符而非分隔符。
我们可用python的正则表达式完成上述要求,但正则的做法不够通用,哪一天,分隔符或引号换成其它符号该怎么办?
为此,参考网上的样例,做了一个通用的escape_split能力,记录如下:
def _try_accept(s: str, pos: int, dest_str: str) -> bool:
return s[pos: pos + len(dest_str)] == dest_str
# in escape mode, sep is ignored
def escape_split(s: str, sep: str, escape_mode_begin: str, escape_mode_end: str) -> list:
ret = []
if not s:
return ret
last_pos = 0
pos = 0
is_escape = False
while pos < len(s):
if _try_accept(s, pos, escape_mode_begin):
pos += len(escape_mode_begin)
if not is_escape:
is_escape = True
elif escape_mode_begin == escape_mode_end:
is_escape = False
elif _try_accept(s, pos, escape_mode_end):
pos += len(escape_mode_end)
is_escape = False
elif _try_accept(s, pos, sep):
pos += len(sep)
if not is_escape:
ret.append(s[last_pos:pos - len(sep)])
last_pos = pos
else:
pos += 1
if last_pos < pos:
ret.append(s[last_pos: pos])
return ret
escape模式的起始和结束标记可以相同,也可以不同。