【问题标题】:Why does this method of detecting keypresses with the Turtle module in Python not work?为什么这种用 Python 中的 Turtle 模块检测按键的方法不起作用?
【发布时间】:2021-06-04 10:58:32
【问题描述】:

已阅读...

How can I log key presses using turtle?

我正在尝试使用稍微不同的方法检测按键。

这是我的代码的简化版本,按预期工作...

from turtle import *

WIDTH, HEIGHT = 500, 500
screen = Screen()
screen.setup(WIDTH, HEIGHT)
bgcolor('grey')
ht()
pu()
    
def checka():
    write('a')
    fd(10)
def checkb():
    write('b')
    fd(10)

screen.onkey(checka, 'a')
screen.onkey(checkb, 'b')

screen.listen()
screen.mainloop()

但是我希望处理所有个字母的按键,所以试试这个...

from turtle import *

WIDTH, HEIGHT = 500, 500
screen = Screen()
screen.setup(WIDTH, HEIGHT)
bgcolor('grey')
ht()
pu()
    
def check(l):
    write(l)
    fd(10)

screen.onkey(check('a'), 'a')
screen.onkey(check('b'), 'b')

screen.listen()
screen.mainloop()

但是这段代码不起作用。 任何人都可以阐明这里发生的事情或提出实现相同目标的替代(但同样简单)方法吗?

【问题讨论】:

  • @quamrana 谢谢!完美的解决方案 - 我需要更多地研究 lambda 函数????????

标签: python python-turtle partial-application


【解决方案1】:

我猜screen.onkey() 需要一个它调用的函数。

您的代码:screen.onkey(check('a'), 'a') 改为调用函数并返回不是函数的 None

您可以像这样使用lambda 创建自己的函数:

screen.onkey(lambda :check('a'), 'a')

如果您想为字母表中的每个字母调用onkey(),那么您可以轻松地进行循环,但不会陷入scope 的问题:

import string

for c in string.ascii_lowercase:
    screen.onkey(lambda c=c:check(c), c)

【讨论】:

    【解决方案2】:

    screen.onkey() 函数需要一个函数作为输入。在您的第一个示例中,您正确地执行了此操作 (screen.onkey(checka, 'a')),但在第二个示例中,您在传递函数之前调用了函数 (screen.onkey(check('a'), 'a')。这意味着您传递的 返回值 check 函数,而不是函数本身。

    您的检查函数没有任何return 语句显式返回值。在 Python 中,不返回值的函数显式返回 None。所以你实际上是在调用screen.onkey(None, 'a'),我猜这没有任何效果。

    要解决此问题,您可以使用闭包 - 函数内部的函数。使用闭包,内部函数可以使用外部函数可用的变量,这意味着您可以为任何字母创建检查函数。

    def make_check_func(l):
        def check():
            write(l)
            fd(10)
        
        return check
    
    screen.onkey(make_check_func('a'), 'a')
    screen.onkey(make_check_func('b'), 'b')
    

    或者,正如 quamrana 建议的那样,您可以使用 lambda 函数以更少的代码完成同样的事情。

    def check(l):
        write(l)
        fd(10)
    
    screen.onkey(lambda: check('a'), 'a')
    screen.onkey(lambda: check('b'), 'b')
    

    --编辑--

    要为字母表中的所有字母添加函数,您可以使用 for 循环。方便的是,Python 已经在string.ascii_lowercase 处定义了一个包含所有小写 ASCII 字符的字符串,因此我们可以使用它来循环。

    import string
    
    def make_check_func(l):
        def check():
            write(l)
            fd(10)
        
        return check
    
    for l in string.ascii_lowercase:
        screen.onkey(make_check_func(l), l)
    

    在这里,for 循环的主体将为字符串中的每个字符运行一次,l 的值就是那个字符。这具有运行screen.onkey(make_check_func('a'), 'a'),然后运行screen.onkey(make_check_func('b'), 'b'),一直运行到screen.onkey(make_check_func('z'), 'z') 的效果。

    请注意,在 for 循环中使用 screen.onkey(lambda: check(l), l) 将不起作用,因为 lambda 函数“记住”的 l 的值将始终为“z”。有关说明,请参阅 common gotchas 条目。

    【讨论】:

    • 这是对正在发生的事情的出色且非常清晰的描述。我觉得 make_check_function 相当有趣,因为我假设它调用 make_check_function 来执行实际操作并传入数据,但是在完成并返回函数名称“check”之后,var 'l' 将没有任何值,所以会写没有。我可以看到函数“lambda”将如何做同样的事情,这有助于我理解这些。如果我想太多,我的头就会开始受伤!谢谢!
    • 因此,按照上述建议,我现在的代码可以按我的意愿工作,但是要重新迭代原始点,是否有更有效的方法来实现与每个代码的 26 行相同我现在拥有的字母,即:screen.onkey(lambda:check('a'), 'a') screen.onkey(lambda:check('b'), 'b') screen.onkey(lambda :check('c'), 'c') screen.onkey(lambda:check('d'), 'd')
    • 我已经用如何在两行代码中注册所有函数的示例更新了我的答案。
    • > 我假设它调用make_check_function 来进行实际操作 这不太对。实际操作由check函数完成,return check返回一个函数对象,而不是函数名。 check 函数“记住”l 的值,即使在 make_check_function 退出之后也是如此。为此,每次运行 make_check_function 时,它都会生成一个不同的 check 函数,并引用不同的 l 值。在内存中,会有几个check函数,而不仅仅是一个。
    • 这在评论中很难解释,所以我推荐阅读 Real Python 文章:realpython.com/inner-functions-what-are-they-good-for
    【解决方案3】:

    虽然这可以使用@quamrana 演示的lambda 解决,或者使用@JackTaylor 详细解释的闭包来解决,但对于此类问题,我偏爱partial

    from turtle import Screen, Turtle
    from string import ascii_letters
    from functools import partial
    
    WIDTH, HEIGHT = 500, 500
    
    def check(letter):
        turtle.write(letter)
        turtle.forward(10)
    
    screen = Screen()
    screen.setup(WIDTH, HEIGHT)
    screen.bgcolor('grey')
    
    turtle = Turtle()
    turtle.hideturtle()
    turtle.penup()
    
    for letter in ascii_letters:
        screen.onkey(partial(check, letter), letter)
    
    screen.listen()
    screen.mainloop()
    

    partial 函数创建一个新函数,其中原始函数的一些参数已被“锁定”。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多