我正在教授编程(在本例中--1对1辅导对编程感兴趣的青少年),这段代码将是进展的最后一个阶段,用于生成Sierpiński三角形的良好图像。
欢迎对此代码进行改进的任何评论!但是,不明确的代码违反标准实践的问题尤其受欢迎,性能问题在这里不太重要。
from PIL import Image
from PIL import ImageDraw
def save_animated_gif(filename, images, duration):
# done using https://pillow.readthedocs.io/en/latest/handbook/image-file-formats.html#saving
first_image = images[0]
other_images = images[1:]
first_image.save(filename, save_all=True, append_images=other_images, duration=duration, loop=0)
def make_pattern(draw, x, y, section_size, remaining_levels):
if remaining_levels <= 0:
return
hole_color = (5, 205, 65)
corner = (x + section_size / 3, y + section_size / 3)
# -1 necessary due to https://github.com/python-pillow/Pillow/issues/3597
opposite_corner = (x + section_size * 2/3 - 1, y + section_size * 2/3 - 1)
draw.rectangle((corner, opposite_corner), fill=hole_color)
parts = 3
for x_index in range(parts):
for y_index in range(parts):
x_anchor = x + section_size * x_index / parts
y_anchor = y + section_size * y_index / parts
new_size = section_size / 3
new_levels = remaining_levels - 1
make_pattern(draw, x_anchor, y_anchor, new_size, new_levels)
def make_carpet(levels, size):
carpet_color = (5, 60, 20)
carpet = Image.new("RGBA", (size, size), carpet_color)
draw = ImageDraw.Draw(carpet)
make_pattern(draw, 0, 0, size, levels)
return carpet
levels = 7
size = 3**levels
carpets = []
carpets.append(make_carpet(0, size))
standard_frame_time_in_ms = 1200
durations = [standard_frame_time_in_ms / 2] # first stage visible for a short time
for i in range(levels - 1):
carpets.append(make_carpet(i + 1, size))
durations.append(standard_frame_time_in_ms)
durations[-1] *= 4 # final stage of animation visible for a long time
save_animated_gif("Sierpiński's carpet.gif", carpets, durations)产出:

发布于 2019-01-30 18:06:07
我只有几个小小的建议:
我喜欢有“调整”,我可能想要更改稍后在我的文件顶部。这使得在不需要挖掘代码的情况下快速修改它们变得更容易了。我会把levels和standard_frame_time_in_ms移到顶端,这样它们就更容易访问了。我还可能将levels更改为n_levels或类似的东西,以使其更清楚地表明它是一个代表有多少个级别的数字,而不是一个“级别”的集合。
现在,您正在使用减半的时间延迟部分填充durations,然后在循环中添加其余部分。不过,在循环中,我并不认为append到durations是一个很好的理由。添加到durations中的数据与循环中可用的数据无关。
我会在循环之前把它填充进去。列表乘法使这很容易。不幸的是,长变量名称很难简洁地完成,但如果需要,可以将其拆分为两行:
durations = [standard_frame_time_in_ms // 2] + [standard_frame_time_in_ms] * (levels - 1)
durations = [standard_frame_time_in_ms // 2] + \
[standard_frame_time_in_ms] * (levels - 1)我还将其更改为使用整数除法(//),因为毫秒的分数可能不会被GIF制造商使用。
我会把整个程序放在最下面的一个函数中:
def main():
carpets = []
carpets.append(make_carpet(0, size))
durations = [standard_frame_time_in_ms / 2] # first stage visible for a short time
for i in range(levels - 1):
carpets.append(make_carpet(i + 1, size))
durations.append(standard_frame_time_in_ms)
durations[-1] *= 4 # final stage of animation visible for a long time
save_animated_gif("Sierpiński's carpet.gif", carpets, durations)现在,您可以在希望main运行时调用它。特别是在使用REPL进行开发时,拥有长期运行的顶级代码可能会很痛苦。您不一定只因为加载了文件就想让整个程序运行。
你有:
carpets.append(make_carpet(0, size))然后在循环中有:
carpets.append(make_carpet(i + 1, size))我不喜欢这样的复制。通常有更好的方法。似乎您可以调整range的界限:
def main():
carpets = []
. . .
for i in range(-1, levels - 1): # Start at -1 instead
carpets.append(make_carpet(i + 1, size))
. . .不过,这基本上只是一个从range到地毯列表的转换。当将一个序列“转换”到另一个序列时,就会想到理解:
carpets = [make_carpet(i + 1, size) for i in range(-1, levels - 1)]然后,如果将来证明这是有益的,那么只需将[]更改为()就可以轻松地使它变懒:
# Now it's a generator that only produces values as requested instead of strictly
carpets = (make_carpet(i + 1, size) for i in range(-1, levels - 1))https://codereview.stackexchange.com/questions/212564
复制相似问题