本文由腾讯 WeTest 授权发布
作者:jhouyang,腾讯资深后台开发工程师。
链接:http://wetest.qq.com/lab/view/113.html
本文由腾讯 WeTest 授权发布,如需转载请联系腾讯 WeTest 获得授权。
WeTest 导读
想要升职加薪,强大的专业能力很重要,好的 UE 要有能力设计好一款 PPT,设计不好就要被嘘。那好的测试呢?好的一些测试会靠一手娴熟的 Python 技巧来升职加薪。本文作者 jhouyang 早年接触多年 Python,通过本文记录早年 Python 的经验之谈,供大家交流学习。
百度 UE 总监在 2016 年国际体验设计大会上演讲被嘘一事已经闹得沸沸扬扬,其被嘘的原因除了主线内容偏题、格局太 low、表达方式糟糕以及插播广告以外,其 PPT 的设计水准和其 UE 总监的身份严重不符成为了大家最为不满的一点。
其 PPT 的标题没有居中,标点符号缺失,色彩搭配硬伤等 PPT 设计中的低级错误让人们对他的专业能力产生了巨大的怀疑。(具体情况大家可以自行搜索 “如何评价百度大 UE 总监刘超在 2016 国际体验设计大会的演讲?”)
说到这里,小编想表达的是,想要升职加薪,强大的专业能力很重要,好的 UE 要有能力设计好一款 PPT,那好的测试呢?
好的测试一般会用 Python 来装逼。
然而,直到我完成 Python 小学一年级课程的时候才发现原来 Python 也有类似的东西。比如:
def foo(val):
if val == 42:
return "you are a programmer!"
else:
return "you are a manong!"
或者你也可以这样写:
def foo(val):
return "you are a programmer!" if val == 42 else "you are a manong!"
友情提示:此特技要小心使用。否则容易暴露你大师的本质。不要像撸主当年,把所有的 if/else 都改成神都看不懂的 "do xxx if xx else xxx"句式。
if isinstance('c', CPlusPlus):
assert(isinstance('python', CPlusPlus))
更变态的时候甚至这样:assert(type('C++') == CPlusPlus))
聪明的你可能会问撸主:“type 和 isinstance 有什么区别啊?”。可你想不到的是撸主会回答你:“自己 google 去 “,然后留给你一个傲娇的背影。
等等!撸主你说这玩意跟 “鸭子” 有什么关系。事实是酱紫的,有一天撸主正在琢磨 “开电脑用左手开机好,还是右手开机好,还是双手齐上好” 这个宇宙终极问题时,一位高年级的大哥哥过来告诉我:“没必要这样,没必要到处 assert isinstance 这种。因为 Python 是鸭子类型。会叫的都是鸭子。”
然后撸主问他 “你会叫吗?”
友谊的小船说翻就翻……
“什么?你不知道鸭子类型?不会自己 google 吗?”
def foo(xxx):
result_list = list()
for ele in org_list:
if "result" in ele:
result_list.append(ele)
BUT,这样做 “一点都不酷!”。要酷还得靠装逼:result_list = filter(lambda ele : "result" in ele, org_list)
不信的话,你就去试试咯:
map(lambda ele : ele + 1, (1, 2, 3, 4))
reduce(lambda x,y:x+y, range(3))
如果高年级的同学问你:小学生?怎么保证埃希不空大?你一定要回答他,最简单的 generator 是这样的:
generator=(i for i in xrange(0, 3))
纳尼?yield 呢?哦,你要 yield 啊,那就这样写:
def generator():
for i in range(3):
yield i
你看,我顺便又介绍了一道幼儿园考题:range 和 xrange 的区别。
那么 iterator 呢?都说了 generator 就是用来生成 iterator 的啦。
generator.next() // 第一个
generator().next() // 第二个
上面只是一个简单的示例,这段简单的代码在实际的工作中并没有什么卵用。
那么它应用的场景有吗?下一次返回的结果依赖于上一次返回的结果。因为 yield 的作用是每次函数调用执行到这里就停止了,下次调用从 yield 后面的语句开始。
比如说树的遍历之类的。
plus2 = functools.partial(add, 2)
plus3 = functools.partial(add, 3)
....
6. 修饰器
1) decorator
有时候撸代码撸累了,想发发微信,找个人帮忙撸代码。你可以试试这样:@xxx 帮我打个日志。XXX就帮你打日志了。
def log(func):
def wrap(*arg, **kargs):
start = time.time()
func(*arg, **kargs)
end = time.time()
print " time = %.6f sec" % (end - start)
return wrap
然后你想要打日志,又懒得撸代码了,就这样:
@log
def foo(arg):
# do something
以下是干货,容易着火。
修饰器的本质就是对函数做些修饰,然后返回一个函数(callable object)。也就是所谓的高阶函数。
因此上面的式子不用语法糖直白的写出来就是:
foo = log(foo)
看到没,foo其实就是一个log返回的callable object wrap的别名。
举个栗子,如果需要这样的修饰器,我们应该怎么写呢?
@decro(1, 2)
def foo(*args, **kargs):
pass
先翻译一下,先把(1,2)传给decro,然后把foo传给decro;然后你返回给我一个能接受(*args,**kargs)参数的函数:
foo = decro(1,2)(foo)
一定要记住,foo是一个callable object。事情就变得很简单了:
def decro(*args, **kargs): # 1,2
def wrap(func): # foo
def _(*args, **kargs): # foo 也必须是一个 callable
func(*args, **kargs)
return _
return wrap
哪里不会点哪里,就是这么简单。装逼技能Decorator GET!
2) wraps
到这里就结束了?如果到这里就结束了,高年级的同学知道了一定又会回来鄙视我们:你试试打印下foo函数,看看是什么?你试着打印一下:
foo
天啦噜!不是foo么,怎么变成"_"了?
别急,这个时候你需要前一节提到的那条裤了:functools,然后对你的 decorator 做一点小小的改动:
from functools import wraps
def decro(*args, **kargs):
def wrap(func):
# 看这里看这里
@wraps(func)
def _(*args, **kargs):
func(*args, **kargs)
return _
return wrap
然后 foo 就变成了:
>>> foo
<function foo at 0x7f4875b77938>
沿着修饰器继续深挖,你可以挖出所谓的函数式编程、闭包一大堆看起来很高大上的概念。可是由于撸主水平有限,只好劳烦您自行 google 啦。
##7. descriptor
至于描述器,多的不多说了。直接看看使用前后效果对比图:
###1)缘起
当时情况是这样的,撸主正在看 clang Python binding 的代码,看到这么一段:
class CachedProperty(object):
def __init__(self, wrapped):
self.wrapped = wrapped
try:
self.__doc__ = wrapped.__doc__
except:
passdef __get__(self, instance, type=None):
if instance is None:
return self
value = self.wrapped(instance)
setattr(instance, self.wrapped.__name__, value)
return value
当时我的心情是这样的:
###2)内置方法
有点流弊啊!虽然我看不懂,但是警察叔叔早就告诉过我 “外事不决问 google“啊。
###3)dict
咱么先从字典说起。看下面一个例子:
class Test(object): passTest.a = 'a'
Test.b = 'b't = Test()
t.c = 'c'
t.b = 'tb'
setattr(t, 'setkey', 'val')print Test.__dict__
print t.__dict__print t.aprint t.c
print t.b
print t.setkey
output 是:
{'a': 'b', '__module__': '__main__', 'b': 'b', '__dict__': <attribute '__dict__' of 'Test' objects>, '__weakref__': <attribute '__weakref__' of 'Test' objects>, '__doc__': None} # 这是Test.__dict__
{'c': 'c', 'b' : 'tb', 'setkey' : 'val'} # 这是t.__dict__
a # t.a
c # t.c
tb # t.b
val # t.setkey
这个例子说明几点:
4)属性查找
当加入所谓的 descriptor 的时,事情变得稍微复杂了一点点。对于一个对象的属性,新的顺序是:
在对象的dict中查找
在类(及其祖先类)的dict中查找 non-data descriptor,存在则返回对应get调用的结果
在类(及其祖先类)的dict中查找普通属性
这样,在原来的属性查找顺序上,我们加上了 non-data descriptor 和 data descriptor,分别插在 2、4 的位置上。
5)回到 descriptor
A:“你说的这么多,跟 descriptor 有什么关系?“
B: "对啊,对啊,一点都没教会我装逼!"
别急,骚年。
回到 1)中的例子,然后再看看 2)中的定义,有木有发现,这其实就是一个 descriptor。这个 CacheProperty 有什么作用呢? 看下面一个使用场景(这个栗子也来自 clang python binding):
class Config:
... @CachedProperty
def lib(self):
lib = self.get_cindex_library()
register_functions(lib, not Config.compatibility_check)
Config.loaded = True
return lib
Config 定了一个 lib 方法,这个方法做一些相对耗时的操作才能获得我们想要的 lib 对象。比如说加载配置文件:有没有办法可以做到只在第一次调用的时候加载配置文件,其他的时候都从缓存里读呢?
看 CacheProperty 的实现:
1 首先它是一个 non-data descriptor;
class DataDesc(object):
def __init__(self, obj):
print 'obj', obj
self.obj = obj def __set__(self, obj, val):
print 'set called', obj, val
self.obj.__name__ = val def __get__(self, obj, type=None):
print '__get__', obj, typeclass Test(object): @DataDesc
def foo(self):
print 'foo'
def func(self):
print 'func't = Test()print t
t.foo = 'c't.__dict__['foo'] = 'c'print t.__dict__print t.foo
当然,这里还有几个细节没有介绍: setattr, getattr 甚至于getattribute, getattr。当你弄明白了所谓的描述器,这些东西都很简单啦!随随便便 google 一大堆。
深度魔法--metaclass
说实话,一开始让我介绍 metaclass,我是拒绝的。因为装逼,有的时候还是要站在巨人的肩膀上。
喏,链接在此,拿走不谢。
http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python
纳尼,你还问我要中文翻译版?
http://blog.jobbole.com/21351/
一本书
如果你只学到第 8 点,可能高年级的同学。还会挑战你:你那么腻害,你知道 GIL 么?
你可能会问:什么是 GIL?然后高年级的同学肯定会鄙视你:你连 GIL 都不知道,肯定没看过源码吧。
你知道我只是介绍装逼特技,so,指望我这种水平去写个 Python 源码剖析出来是不现实的。不过我可以推书啊《Python 源码剖析:深度探索动态语言核心技术》
据说下雨天,看书跟看源码更配哦。
看完此书,你的装逼领域又可以扩展到 C 了哦。还可以学会一点怎么让 C 也面向对象,告别汪星人了。
自此,妈妈再也不用担心学 C 的找不到对象啦!
由于水平有限,文章缪误之处,请不吝指出!多谢!
本文由腾讯 WeTest 授权发布
作者:jhouyang,腾讯资深后台开发工程师。
链接:http://wetest.qq.com/lab/view/113.html
本文由腾讯 WeTest 授权发布,如需转载请联系腾讯 WeTest 获得授权。