为什么这种使用Python中的Turtle模块检测按键的方法不起作用?

Why does this method of detecting keypresses with the Turtle module in Python not work?

已阅读...

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()

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

我猜 screen.onkey() 接受它调用的函数。

您的代码:screen.onkey(check('a'), 'a') 而不是调用函数和 returns None 这不是函数。

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

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

而如果你想为字母表中的每个字母调用 onkey(),那么你可以很容易地有一个循环,但不会陷入 问题:

import string

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

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

您的检查函数在 return 值中没有任何 return 语句。在 Python 中,不 return 显式 return 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 条目以获取解释。

尽管这可以像@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 函数创建了一个新函数,其中原始函数的一些参数已被“锁定”。