知乎上一则python最炫代码解析
Contents
在知乎的一则问题有哪些能炫技的代码写法?中看到一个回答:使用python输出”hello world”,
看完回答确实很炫。
注: 原回答中的代码需要申请,如果有侵权会立即删除。
0x00 介绍
代码如下:
1 |
|
输出结果为:
1 |
|
0X01 涉及的概念
1. lambda表达式
简单来说,编程中提到的 lambda 表达式,通常是在需要一个函数,但是又不想费神去命名一个函数的场合下使用,也就是指匿名函数.python中的lambda表达式具体可参考:Lambdas
Example:
1 |
|
在上面的代码中,就是使用了lambda表达式使得代码成为一行,当然这只是炫技的一部分。其实,只要你功能不是太大,那用lambda都可以包裹到一行。有一个这样的库,可以把你所有python实现,包裹到一行当中,请见csvoss/oneliner · GitHub
2. python 内置函数
1). __class__
内置函数
用于获取对象的class
Example:
1 | print(True.__class__) |
具体可参考官方API
2). __name__
内置函数
用于获取当前对象的名字
1 | print(True.__class__.__name__) |
具体可参考官方API
3). ** __import__
内置函数**
在官方给出的API中:
_import__(name[, globals[, locals[, fromlist[, level]]]])
这个函数被import语句调用,作用是引入名字为name的模块
4). getattr
内置函数
在官方给出的API中:
getattr(object, name[, default])
返回所给对象object的名字为name的值
Example:
1 |
|
5). func_code.co_nlocals
内置函数
这个函数(python2.x)的作用是获得函数的参数的个数
具体可以查看官方的API
Example:
1 | func = lambda _:_ |
在该代码中多次使用这个技巧来获得整数的值
0x02 炫技
1.炫技一 获取目标类名
在该代码中使用截取一些对象的类的名字获取目标的类名。
首先在最外层的lambda的标的式中一共有8个参数,参数依次以18个8的整数,这个将在后面提到。所以_
组成,而传入的参数即1_
的个数代表整数的大小。通过上面提到的内置函数,我们将_
代表的数字带入可以得到
1 | print(True.__class__.__name__[1] + [].__class__.__name__[2]) |
通过一系列类的名字截取出我们所需要的os中的write的名字,并通过__import__
和getattr
获得write
函数。
炫技二 递归
在该代码中有几处地方使用的递归的方式
其形式为:
1 | lambda x,y:x(y):((lambda x,y:x(y)),y) |
如果读者非常熟悉递归想必很容易看出来这个形式的。
当然递归中需要有退出的条件,例如在上面获得字符串的递归中,使用了用第___//__
(第三个参数//
第二个参数),直达为0退出。
注://
运算即除法取商 如5//4=1 4//2=2 2//3=0
炫技三 获得字符串
我们所拥有的参数只有1-8,通过1-8的数字使用移位运算和加法等基本的运算得到一个目标值(复杂技巧),然后对该目标值递归的方式依次获得目标字符的ascii通过chr函数转化为字符。这部分代码位于整个代码的中间部分即getattr()获得的write
函数包裹的范围。
通过计算这个数为:249642804582896990794842262078881781526671259605700207976
然后通过递归获得目标字符串,递归退出的条件为第传入的值为0,在每次递归中这个值都要//(1<<8)(_<<________)
。在这个递归函数中,第二个参数为256,第三个参数即上面提到的。我们改写这个复杂的函数可以得到
1 | number = 249642804582896990794842262078881781526671259605700207976 |
炫技四 获得1-8的数字
上面已经提到可以通过co_nlocals
的内置函数来获得整型数,如何向最外层的lambda表达式传入1-8的参数呢?这里同样使用了递归的方式。每次生成一个数字的数组并与前面生成的数组相加,递归的退出条件也是当传入的参数为0的时候。最后生成一个包含[1-8]的数组,传入匿名函数中。这部分代码也就是最后的那部分代码,也就是最外层lambda的括号外面。将其转化为常规代码即:
1 | def getArray(getNumber,lambdaArray): |
这样就可以获得了[1-8]的数组,当然使用数组传入函数的8个参数时,需要使用*
值来使得正常的传入值。有关这部分的内容可以参考这篇博客-python函数的四种参数传递方式或者官方文档。
0x03 总结
整个代码可以大体分为三大部分:
第一部分: 最外层的匿名函数,接受1-8个参数分别代表1-8的整数
第二部分: 利用递归的形式获取目标字符
第三部分: 利用递归获得[1-8]的数组
每一部分都是用了不同的技巧使得简单的代码复杂化,虽然整个代码初看非常繁琐复杂,但是通过一一解析还是很容易理清其中逻辑的。
如果上面的分析中存在错误请不吝赐教。
(完)