一些好玩的程序
打印自身的程序
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)