之前看过很多python一行代码的文章,但对其中原理并不十分了解,相信其他人也有同样问题。这次就来选取一些有趣的一行代码,并深入分析其中的原理~
0. 预备知识
range()函数
range(start, end, step) 生成[start, end)范围内的数,每个数相隔step。start默认为0,step默认为1。注意区间是左闭右开,即包含start,不包含end。
列表推导式
[元素 for i in range(n) if 条件] 列表推导式的结果是一个列表。
for i in range(n)的作用可以理解为生成n次元素,if又对元素进行了限制。
先看个简单的[i for i in range(3)]结果是[0, 1, 2]。一共生成3次元素,第i次生成元素i。所以:第0次生成元素0,第1次生成元素1,第2次生成2。没有if限制,那么所有元素都保留。
再看个稍难的[i*i for i in range(6) if i%2!=0]结果是[1, 9, 25]。if i%2!=0限制了i必须是奇数,那么i只能是1,3,5。表达式是i*i,所以结果就是1,9,25。
lambda表达式
lambda *args:return_value
lambda表达式又称为匿名函数,没错它就是个没有名字的函数。。
*args是参数,可传入任意个数。retuan_value是函数的返回值。
比如算2*x+1,那么需要的参数就是x,返回结果就是2*x+1。
g(3)=7。是不是和数学的表达一样?
filter过滤器
filter(Function, iterable object)
Function是一个函数,iterable object是一个可迭代对象。
filter功能是每次从可迭代对象中取出一个元素,作为Function的参数运算,如果Function的返回值是False,那么该元素就会被丢弃,如果返回值是True,就会被保留。
lambda表达式和filter一起用就很强大了!
看个简单例子filter(lambda x: x %2, range(3))。从range(3)中筛选对2取余为真的数(奇数),结果是1,3。
True和False
在python里面,0,空字符串,空列表,空元组会被认为是False。
or和and
如果某个数能够完全确定表达式的值了,就把这个数返回,作为整个表达式的值。
or表达式左右两边,只要一边为真,表达式就为真。因此在判断中,如果左边为真,直接返回左边,没有必要再判断右边。
比如2 or 3 = 23,因为根据左边的2判断出表达式为真,无论右边是什么都不影响,可以直接返回2。
and表达式左右两边都为真,表达式才为真。因此在判断中,如果左边为假,可以判断表达式为假,直接返回左边,没有必要再判断右边。如果左边为真,就要再判断右边。
比如3 and 2 = 2。左边是3,还需要判断右边,右边是2,所以返回2。
分片
对字符串的分片可以理解为截取子字符串。
s[start:end]的结果是s的子字符串,从第start个元素开始,长度为end-start,不包括第end个元素。start默认为0,end默认为s的长度。若start>=end,则长度=end-start<=0,只能返回空字符串。
列表分片的分法也是一样的,只不过返回的是一个列表。
join()
s.join([...])。s和str列表内的元素都是str类型。join函数把s加入到列表相邻两个元素中,组成一个新的字符串。
初学者应该都会学习这些基础的python知识。东西还是挺多的。
1 hello world
print("hello world!")
没错就是hello world!大家可以回想一下学习c和java时候输出一个hello world有多困难?!而用python这一行就搞定。
python里面函数是一等公民,Java里面类是一等公民,它们的设计理念是不一样的。python对比java,就不需要又public又class的,只需要调用内置函数print()就行。
2 求素数
[prime for prime in filter(lambda x: not[i for i in range(2, x) if x%i==0], range(2, 101))]
这么鬼畜一长串能看懂??后面有更长的!
最外层是个表列推导式,保存了求出来的素数,那么看filter是怎么筛选的就行了。
filter里面的range也不用看,只是个范围而已,那么重点只剩下:
lambda x: not[i for i in range(2, x) if x%i==0
假设x是一个素数,那么range(2, x)的范围内x%i 一定是不等于0的。于是一直不满足if的条件,推导式得到一个空列表[]。加个not,not [] = True。
素数->得到空列表[]->not []->True。也就是素数才会满足filter的条件!
filter筛选出来的就是素数。如果lambda里面没有not呢,那么筛选出来的就是非素数。
3 FizzBuzz问题
3的倍数替换成Fizz,5的倍数替换成Buzz,既是3的倍数又是5的倍数替换成FizzBuzz。
["fizz"[i%3*4:]+"buzz"[i%5*4:] or i for i in range(1, 100)]
这是个列表推导式。or i 好懂,如果前面都不为真,那么返回i。
如果i是3的倍数那么i % 3 * 4 = 0,对"Fizz"的切片就是"Fizz"[0:]="Fizz"。否则start的范围会超过"Fizz"的长度4,切片失败,返回''。
对"Buzz"的切片同理。如果i既是3又是5的倍数,就会得到"Fizz"+"Buzz"="FizzBuzz"
4 打印99乘法表
print('\n'.join([' '.join(['%s*%s=%-2s' % (y, x, x*y) for y in range(1, x+1)]) for x in range(1, 10)]))
两个for可理解为两层循环,感觉这个很好懂没什么好过多解释的??
5 打印爱心
print('\n'.join([''.join([('iloveu'[(x-y)%6]if((x*0.05)**2+(y*0.1)**2-1)**3-(x*0.05)**2*(y*0.1)**3<=0 else' ')for x in range(-30,30)])for y in range(15,-15,-1)]))
这是最高能的一个!结果还是很好看的。
下面研究这一行十二分鬼畜的代码究竟是怎么回事。
这一行太长了,我们可以把两个列表提取出来,便于观察。
再把列表推导式写成循环的形式。
逻辑清晰之后,用正常人的思路把功能实现。
对于if里面那一串公式,估计就是心形线的函数了,小于等于0表示该点(x, y)在心形里面,所以从"iloveu"中选字符填充,否则就用' '填充。
至于为什么取下标为(x-y)%6,%6很好理解,因为'iloveu'的长度就是6嘛。(x-y)我原以为是作者故意弄复杂的,用x%6试了结果是这样的:
单以x%6选择下标导致不同行的同一个位置都是一样的字符(和上面的心对比一下)。所以(x-y)是做了一个偏移,视觉效果更好了。
颜色也最好调一调。。这颜色太丑了
6 其他想法
有的一行代码已经违背了python简洁易读的初衷,但其中对于内置函数的妙用和透彻理解是值得学习的。
最后一个打印心形的方法,换一个判断函数就能打印其他形状了。填充的字符也可以改,只要把长度也改成对应的就行。
水平有限,如有错误,欢迎交流和批评指正!转载注明出处即可。
转自公众号「小z的笔记本」
每周更新各种内容
包括但不限于Python、爬虫、其他骚操作
欢迎关注