python深度讨论

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模式的起始和结束标记可以相同,也可以不同。

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值