Skip to main content

一些好玩的程序

打印自身的程序

Quine 算法

如果程序保存在文件中,最简单的方式是让程序读取自己所在的文件,然后把它打印出来:

print(open(__file__).read())

如果程序只在内存中,或者不可以调用文件读写函数,那么可以采用 Quine 算法,也叫“自产生程序”。它是以美国哲学家奎恩(Willard Van Orman Quine)命名的算法。它的工作原理大致如下:

  • 把程序划分成两个主要部分: A 和 B
    • 我们先定义一个函数 Q,对于字符串 A,Q(A) 在执行后会变成字符串 B。
    • A 部分是用字符串表示的 B 部分的代码
    • B 部分的代码可以接收一段字符串 A,然后调用函数 Q 计算出 Q(A),然后打印出 A 和 Q(A)

repr() 函数

在编写 Python 代码之前,先要介绍一下 Python 自带的 repr() 函数。repr() 函数运行后返回一个输入对象的“官方”字符串表示,这个字符串通常可以用来重新创建该对象。其主要目的是调试和开发。

repr() 的输出主要是给开发者看的,其目的是明确无误地表达对象的类型和(最关键的)特征。不同于 str() 函数,str() 更注重于可读性,而 repr() 更注重于明确性和一致性。如果输入的对象是 Python 内置类型,这个字符串可以直接用 Python 表达式来计算得到相应的对象。

比如运行下面的程序,可以看出 repr() 的特点:

x = "abc"
print(str(x)) # 输出是没有引号的,这就是字符串的打印结果: abc
print(repr(x)) # 输出是带有引号的,表示如果的对象是一个字符串: 'abc'

对于我们自己定义的对象,可以通过在类中定义 __repr__() 方法来为自定义对象实现 repr() 函数的效果。当 repr(obj) 被调用时,Python 会寻找 obj 的类定义中的 __repr__() 方法,并执行它。比如:

class Test:
def __init__(self, value):
self.value = value

def __repr__(self):
return f'Test({self.value!r})'

# 创建 Test 对象
obj = Test('hello world')

# 使用 repr() 函数
print(repr(obj)) # 输出:Test('hello world')

# 内置类型的例子
print(repr(123)) # 输出:'123'
print(repr([1, 2, 3])) # 输出:'[1, 2, 3]'

在这个例子中,Test 类定义了 __repr__() 方法,该方法返回一个格式化字符串,展示了如何创建一个与当前对象具有相同值的新对象。这种做法提高了代码的可读性和可维护性,尤其是在调试时。

Python 的自产生程序

借助 repr 机制,我们可以在 Python 中轻松的编写一段自产生程序:

x = 'y = "x =" + repr(x) + "\\n"\nprint(y+x)'
y = "x =" + repr(x) + "\n"
print(y+x)

上面这段代码中第一行是 Quine 算法的 A 部分,它定义了一个用字符串表示的 B 部分的代码。程序的后两行是 B 部分。

Python 中,还可以使用 %r 这个字符串格式化符号,隐式调用 repr() 函数,把一个对象直接嵌入到一段字符串中去。使用 %r 格式化符号,可以让自产生程序更简洁:

x='x=%r;print(x%%x)';print(x%x)