您确实希望自动化这一点,而不是手动执行;有一些工具,如decompyle和{}可以从字节码生成Python源代码。在
代码对象中的字节码有些混乱,我们缺少代码对象中的co_argcount和{}信息。不过,我很确定清单的理解应该是:YSay = [YSag(YSao, lambda s: s[x], x) for x in YSaP(0, YSaO)]
字节码
^{pr2}$
从上到下转换为具有x、lambda、YSao和{}的堆栈,CALL_FUNCTION按相反顺序将前3个传递给最后一个,因此调用YSag(YSao, , x)。在
lambda从第27行加载,其字节码为:27 0 LOAD_FAST 0 (s)
3 LOAD_GLOBAL 0 (x)
6 BINARY_SUBSCR
7 RETURN_VALUE
这意味着s是lambda的参数(它是一个加载了LOAD_FAST的局部变量),而{}是一个全局变量,所以这就转换成了lambda s: s[x]。在
注意,CALL_FUNCTION_VAR使用*args调用功能,因此您需要将第9行更正为:YSas = YSam(*[YSaN(l) for l in YSaW])
这是一种冗长的拼写max(len(l) for l in YSaW)的方法,但是列表理解扩展为单独的参数,而不是作为单个参数传入的生成器表达式。在
我发现使用^{} function和^{}来查看我对字节码的解释是否正确是很有帮助的;输入一个表达式或语句,输出应该与您的字节码大致匹配(使用行号和字节码编号偏移量):from dis import dis
dis(compile(string, '', 'exec'))
例如,对于最后一行,我用以下方法验证了结果:>>> dis(compile('YSas = YSam(*[YSaN(l) for l in YSaW])', '', 'exec'))
1 0 LOAD_NAME 0 (YSam)
3 BUILD_LIST 0
6 LOAD_NAME 1 (YSaW)
9 GET_ITER
>> 10 FOR_ITER 18 (to 31)
13 STORE_NAME 2 (l)
16 LOAD_NAME 3 (YSaN)
19 LOAD_NAME 2 (l)
22 CALL_FUNCTION 1
25 LIST_APPEND 2
28 JUMP_ABSOLUTE 10
>> 31 CALL_FUNCTION_VAR 0
34 STORE_NAME 4 (YSas)
37 LOAD_CONST 0 (None)
40 RETURN_VALUE
对于函数对象,您希望从给定的co_consts条目(compile(...).co_code.co_consts[an_index])中提取代码对象,或者先创建函数,然后将函数对象传递给dis.dis():>>> dis(lambda s: s[x])
1 0 LOAD_FAST 0 (s)
3 LOAD_GLOBAL 0 (x)
6 BINARY_SUBSCR
7 RETURN_VALUE
最后你得到了一个相当糟糕的软件,它把文件中的字符混在一起。我已经清除了混淆,并使用了更为惯用的Python来实现我认为会产生相同的输出:import sys
def rotn(s, n): return s[n:] + s[:n]
with open(sys.argv[1]) as inf:
lines = [l.strip().replace(' ', '.') for l in inf]
maxlength = max(len(l) for l in lines)
padded = (l.ljust(maxlength, '.') for l in lines)
swapped = [rotn(s, len(s) // 2) for s in padded]
cols = []
for x, col in enumerate(zip(*swapped)):
offset = (x % len(col)) * (-1 if x % 2 else 1)
cols.append(rotn(col, offset))
for row in zip(*cols):
print ''.join(row)
因此,用.将所有被剥离的行填充成相等的长度,交换行的开始和结束,然后将结果文本块中的每个列向上或向下旋转列号(每列交换方向),然后显示结果文本。在
我怀疑使用'.'代替空格在这里也不是真正必要的;放弃.replace()调用并留下{}使用默认的空格填充,结果基本相同,但空格保持不变。在