我刚刚用Python写了一个雪动画。我认为这很干净,但是我有一些我不喜欢的东西。

from random import randrange
import time

# Snow animation
# Snow is simply the `#` symbol here. Half of the snow moves at 1 char
# per frame, while the other half moves at 0.5 chars per frame. The
# program ends when all the snow reaches the bottom of the screen.
# The viewing area is 80x25. It puts 100 snow flakes to output, half
# fast, and half slow. Every frame it dispenses 4 flakes, 2 fast and
# 2 slow ones, at random locations at the top of the viewing area.

screen = {'x': 80, 'y': 20}
drops = []

def createRainDrop(x, y, speed):
    return {'x': x, 'y': y, 'speed': speed}

def createRandomDrops():
    dropCount = 4
    for i in range(dropCount):
        yield createRainDrop(randrange(0, screen['x']), 0, min((i % 2) + 0.5, 1))

def moveDrops():
    for drop in drops:
        speed = drop['speed']
        drop['y'] = drop['y']+speed

def drawDrops():
    out = [''] * screen['y']
    for y in range(screen['y']):
      for x in range(screen['x']):
        out[y] += '#' if any([drop['x'] == x and int(drop['y']) == y for drop in drops]) else ' '

    return '\n'.join(out)


def dropsOnScreen():
    return any([drop['y'] < screen['y'] for drop in drops])

drops += createRandomDrops()

while dropsOnScreen():
    if len(drops) < 100:
        drops += createRandomDrops()

    print(drawDrops())
    moveDrops()
    time.sleep(0.100)


例如,我不知道如何删除重复的行drops += createRandomDrops()drawDrops()有点像黑客。

我承认!写这篇文章的时候是下雨,不是下雪!

评论

createRainDrop您确定要下雪吗?

不幸的是。写这篇文章的时候是下雨,不是下雪。

我很想看动画。我需要遵循什么才能在Windows 7计算机上“加载”代码?我可以只安装Python并让文件运行它吗,还是需要一个IDE在其中运行它?任何关于tut的链接都将很不错。目前这里是“下雪”,半雪半雨。

只需安装python 3.x,进入控制台并输入python C:\ path \ to \ snow.py。您可能需要缩小控制台窗口以使动画更流畅。

#1 楼

让我们看一下代码。

from random import randrange
import time


您的导入非常少!好。

# Snow animation
# Snow is simply the `#` symbol here. Half of the snow moves at 1 char
# per frame, while the other half moves at 0.5 chars per frame. The
# program ends when all the snow reaches the bottom of the screen.
# The viewing area is 80x25. It puts 100 snow flakes to output, half
# fast, and half slow. Every frame it dispenses 4 flakes, 2 fast and
# 2 slow ones, at random locations at the top of the viewing area.


对我来说,这看起来像是文档字符串。这样渲染它会很好。您可以通过删除#符号并将其括在"""引号中来实现此目的。但这是一个简单的文件,所以也许我们现在就可以像这样保留它了吗?

screen = {'x': 80, 'y': 20}
drops = []


我认为类似类的东西对此更好。让我们尝试一下

def createRainDrop(x, y, speed):
    return {'x': x, 'y': y, 'speed': speed}


当然,现在我们需要用createRainDrop(...)替换RainDrop(...),并用drop['...']替换drop....。 br />更好。

class RainDrop(object):
    def __init__(self, x, y, speed):
        self.x = x
        self.y = y
        self.speed = speed


我们在这里修改drop,而不是要求它修改自己。我们应该在此处编写类似drop.moveDown()drop.tick()(“ tick”是通常用来通知事件有关时间提前的事件)的内容。

对于屏幕上的所有位置,您正在遍历所有滴。理想情况下,我们应该解决以下问题:

def createRandomDrops():
    dropCount = 4
    for i in range(dropCount):
        yield RainDrop(randrange(0, screen['x']), 0, min((i % 2) + 0.5, 1))


现在,它更快更干净了。说得通。除非我建议不要使用[...],它会创建一个列表。更好地使用

def moveDrops():
    for drop in drops:
        drop.y = drop.y + drop.speed


行为相同,但不必创建中间列表。

def drawDrops():
    out = [''] * screen['y']
    for y in range(screen['y']):
      for x in range(screen['x']):
        out[y] += '#' if any([drop.x == x and drop.y == y for drop in drops]) else ' '

    return '\n'.join(out)


您想摆脱对drops += createRandomDrops()的重复调用。

评论


\ $ \ begingroup \ $
感谢您的回答!看起来确实比我的原始代码好得多。一个小问题:在createRandomDrops中,您指出min((i%2)+ 0.5,1)并将其更改为min(int(i%2 + 0.5),1),原始对象实际上打算使用浮点数,将数字更改为1或0.5,这样我就可以得到半步的雪花,尽管很难分辨...
\ $ \ endgroup \ $
– J阿特金
16年2月1日在16:30



\ $ \ begingroup \ $
@JAtkin:蛇壳,但应该是PascalCase的类除外。
\ $ \ endgroup \ $
– Sjoerd Job Postmus
16年1月1日在20:00

\ $ \ begingroup \ $
@SjoerdJobPostmus我不确定OP的意图是关于速度。看起来应该是0.5或1,所以也许(1.0,0.5)[i%2]
\ $ \ endgroup \ $
– njzk2
16-2-1在23:31



\ $ \ begingroup \ $
@ njzk2:重新读取后,语句为min((i%2)+ 0.5,1)。这取值为(1.0,1.5)[i%2]。它可能不是预期的,但它是当前值。
\ $ \ endgroup \ $
– Sjoerd Job Postmus
16年2月1日在23:41

\ $ \ begingroup \ $
@Schmuddi:请考虑out = [[''] * 7] * 5,然后考虑out [3] [4] ='X'。然后,out [2] [4]也将设置为X,因为out [0] .. out [4]都引用相同的列表。一个有效的替代方法可能是out = [[''] * screen [x'] for _ in range(screen ['y'])]。
\ $ \ endgroup \ $
– Sjoerd Job Postmus
19年1月28日在15:26

#2 楼

很棒的动画!

让我们摆脱烦恼。根据PEP 8,您应该一致地使用4个缩进空间,并且函数名称应为snake_case。设计的主要缺点是可伸缩性。如果将循环扩展为无限期运行,则最终会遇到性能问题。

一个问题是drops列表随每次迭代而增长,并且永远不会被修剪。水滴在掉落到地面后不会消失。他们会永远消失在屏幕外。解决方案是让moveDrops()跌落到最低点以上时删除它们。 (与让dropsOnScreen()重新检查每个动画帧上的每个液滴相比,这是一种更明智的策略。)

此外,要将液滴放置在网格上,您还需要对每个液滴进行O(n)扫描屏幕与'#' if any([drop['x'] == x and int(drop['y']) == y for drop in drops])。我将重写drawDrops(),以便使用字典或2-D数组将每个放置位置放置。与重复的追加操作相比,我也更喜欢使用理解,但这主要是样式首选项。但您的代码显示为screen = {'x': 80, 'y': 20}。理想情况下,应使用curses库在运行时检测尺寸。由于screen用作全局变量,因此我希望看到它名为SCREEN并使其不可变。 namedtuple将使其不可变,具有允许点访问器而不是笨拙的[]表示法的其他好处。我认为widthheightxy更合适。

类似地,为雨滴定义类将避免使用drop['x']表示法。此外,createRainDrop()函数强烈要求它是一个构造函数。

创建拖放并循环

其余的代码是Python迭代中的一个练习。一切都可以通过自由使用迭代器来处理。

createRandomDrops()中,使用min((i % 2) + 0.5, 1)代替神秘的公式itertools.cycle([0.5, 1])。我将把createRandomDrops()变成无限生成器。

在下面的解决方案中,可以通过修改drop_paramsprecipitation集中调整速度,强度和持续时间等参数。例如,precipitation = drop_generator(**drop_params)将导致无限循环,每帧仅出现一个新的下降。

评论


\ $ \ begingroup \ $
非常好!显然我的密码公式非常糟糕,因为它返回0.5和1 ... codereview.stackexchange.com/questions/118538 / ...我个人非常不喜欢snake_case(我来自Java / Scala),这就是为什么我使用骆驼香烟盒。编辑,哇,我几乎看不懂您的解决方案。我要去找我的Python书...
\ $ \ endgroup \ $
– J阿特金
16-2-2在13:35



\ $ \ begingroup \ $
@JAtkin,因为您使用的是Python3,为什么不只使用内置的终端大小?
\ $ \ endgroup \ $
–Wayne Werner
16年3月3日在22:07

\ $ \ begingroup \ $
2个原因。 1)我不知道。 2)我通过反复试验得出了当前数字,以使机器上的闪烁最少。
\ $ \ endgroup \ $
– J阿特金
16年2月3日在22:54

\ $ \ begingroup \ $
好的答案。也许还使用if __name__ ==“ __main__”卫队包装代码?
\ $ \ endgroup \ $
–omgimanerd
17年7月19日在0:36

#3 楼

你没有下雪!显然应该是

  for flake in flurry:


评论


\ $ \ begingroup \ $
对于为什么(在我看来)学步鞋是这个问题的第二个最不满意的答案,我有点困惑。诚然,英语词汇差异对下一个代码维护者很重要,关于2个变量名的100个字符的答案似乎没有这里的其他答案有价值。 CR社区,您让我感到困惑。
\ $ \ endgroup \ $
–克里斯·西里菲斯(Chris Cirefice)
16年3月3日,在2:38

\ $ \ begingroup \ $
@ChrisCirefice您不是唯一对此感到困惑的人。如果您愿意,请随时在我们的meta上发布问题。不幸的是(?),挑剔变量名是一个有效的答案。至于为什么它被这么大的批评,我不能说。
\ $ \ endgroup \ $
–西蒙·福斯伯格
16-2-3在13:29

\ $ \ begingroup \ $
@ChrisCirefice和Simon很好,最初的问题是在脚本中询问一些顽皮,这些脚本会在屏幕上画雪。大多数人可能都没有看到那种严重的气氛
\ $ \ endgroup \ $
–我很困惑
16-2-3在13:47



\ $ \ begingroup \ $
事物的命名是计算机科学中的两个难题之一,因此“选择尼特”的名字非常重要。作为参考,困难的问题是1)命名事物2)缓存过期3)一次出错。我认为这次提名也增加了一两个。
\ $ \ endgroup \ $
–低音扬声器
16-2-3在13:49

\ $ \ begingroup \ $
@SimonForsberg我在Meta上发布了一个问题。我不认为应该删除此答案或进行任何其他操作,但是我认为这样的答案似乎很有趣,应该是真正的评论,而不是其他完整答案。
\ $ \ endgroup \ $
–克里斯·西里菲斯(Chris Cirefice)
16年3月3日在17:19

#4 楼

我很惊讶没有人谈论您的角色选择!他们的一堆标签为什么掉下来?不,我在说,这是可以选择的角色,但我们可以做得更好!如何将#更改为unicode(Python 3支持!)。现在看起来真的像雪!

此外,您当前的代码与Python 2向后兼容。更改字符会破坏这一点。如果要修复该问题,请添加:

# -*- coding: utf8 -*-


到文件顶部。

评论


\ $ \ begingroup \ $
如果您不确定如何键入或不想搜索,可以使用print('\ N {SNOWFLAKE}')帮助。如果您使u'\ N {SNOWFLAKE}';)也​​可以在Python2上使用
\ $ \ endgroup \ $
–Wayne Werner
16-2-3在22:11

\ $ \ begingroup \ $
很好,我不知道utf-8有雪的符号。
\ $ \ endgroup \ $
– J阿特金
16年2月4日在16:37

#5 楼

def createRandomDrops():
    dropCount = 4
    for i in range(dropCount):


代码中间的魔术数字4

def drawDrops():


我希望这种方法实际上会绘制液滴,而不是将字符串返回打印

#6 楼

from random import randrange


我建议使用

import random


然后在您的代码中使用random.randrange。在您的特定情况下,也许并不重要,但是我自己发现,在可能的情况下,导入模块而不是从模块中导入名称是一个好规则。 google.github.io/styleguide/pyguide.html#Imports


仅对软件包和模块使用import


导入模块有时会有所帮助您可以防止循环导入错误。

它有助于在模块扩展时导入太多名称。

它可以帮助您解决名称冲突。例如,几个模块具有ValidationError类(mongoenginewtforms等),并且您的代码使用所有这些模块。