在python中按不同的键由不同的对象调用相同的函数

Calling same function by different objects by pressing different key in python

提问人:Akash Singh 提问时间:10/31/2023 最后编辑:Akash Singh 更新时间:11/1/2023 访问量:57

问:

我使用 turtle 模块在 python 中创建了两个,分别说“tur1”和“tur2”。我在屏幕上创建了事件侦听器。我创建了一个函数“move_fwd(turte_name)”,它将向前移动 20 步。

我希望如果按下键“w”,那么这个方法应该被“tur1”调用,tur1应该移动,如果按下键“i”,那么这个函数应该被“tur2”turtle调用,它应该移动。

我不知道如何在event_listener调用此方法时传递参数“turtle_name”。

from turtle import Turtle, Screen

screen = Screen()
tur1 = Turtle()
tur1.shape("turtle")
tur1.shape("turtle")
tur1.penup()
tur1.setposition(x=-200, y=-100)

tur2 = Turtle()
tur2.shape("turtle")
tur2.penup()
tur2.setposition(x=200, y=100)


def move_fwd(turtle_name):
    turtle_name.forward(20)


screen.listen()

screen.onkey(move_fwd, "w") # how to tell that this is to be called for tur1
screen.onkey(move_fwd, "i") # how to tell that this is to be called for tur2

screen.exitonclick()

我有一些想法如何通过添加addEventListener在javascript中做到这一点。


document.addEventListener("keypress", function(event) {

  makeSound(event.key);

  buttonAnimation(event.key);
  
  });


function makeSound(key) {

  switch (key) {
    case "w":
      var tom1 = new Audio("sounds/tom-1.mp3");
      tom1.play();
      break;

    case "a":
      var tom2 = new Audio("sounds/tom-2.mp3");
      tom2.play();
      break;

    case "s":
      var tom3 = new Audio('sounds/tom-3.mp3');
      tom3.play();
      break;


但是不知道如何在 python 中做。

感谢您的宝贵时间。

python 参数传递 事件侦听器 python-turtle

评论


答:

2赞 quamrana 10/31/2023 #1

您只需要调用两个不同的函数:

def move_fwd1():
    tur1.forward(20)
def move_fwd2():
    tur2.forward(20)


...

screen.onkey(move_fwd1, "w") # how to tell that this is to be called for tur1
screen.onkey(move_fwd2, "i") # how to tell that this is to be called for tur2

2赞 Barmar 10/31/2023 #2

使用lambda

screen.onkey(lambda: move_fwd(tur1), "w")
screen.onkey(lambda: move_fwd(tur2), "i")

评论

0赞 Claudio 10/31/2023
@AkashSingh:你不需要使用它,这会带来复杂性,而且并不容易理解为什么它在这里按预期工作。查看另一个答案,建议一种更简单的方法来达到相同的效果。lambda
0赞 Barmar 10/31/2023
@Claudio 这种方法比为每只定义一个命名函数更能概括。
0赞 Barmar 10/31/2023
如果有多个操作可以绑定到键,则必须为每个操作编写两个函数。
0赞 Claudio 10/31/2023
@Barmar:请参阅我的回答,并认为这是对您的评论的回应,不适合这里。这肯定是一个品味问题,但在我看来,lambda 结构有其缺陷,尤其是对于初学者。顺便说一句:根据我迄今为止的所有编程经验,我倾向于使用自解释的不同函数名称,而不是相同的函数名称,并且您必须在文档中查找大量可能的选项和参数。在我看来,这更接近我们的思维方式,更接近于自然语言的使用。
0赞 Claudio 10/31/2023
请参阅我的答案,了解 turtle.py 模块的非常简单的修补,然后允许事件处理程序函数知道按下了哪个键,因此您不需要解决 turtle.py 模块附带的问题,即未将按下的键传递给事件处理程序函数。
0赞 Claudio 10/31/2023 #3

这个答案是由 Barmar 的评论触发的,他解释了使用该结构的优点:lambda

@Claudio 这种方法比为每只定义一个命名函数更能概括。

我建议使用另一种方法进行泛化,该方法允许使用 Python 列表定义任意数量的海龟及其属性,然后用于生成提供所需功能所需的代码。是的,下面的代码对不同的键盘键使用不同的函数名称,但同时也允许修改用于所有海龟的函数。move()

from turtle import Screen, Turtle
screen = Screen()

Turtles         =   [Turtle(),  Turtle()    ]
StartPositions  =   [(200,-100),(200,100)   ]
Names           =   ["turti_1", "turti_2"   ]
Keys            =   ["w",       "i"         ]
Colors          =   ["red",     "green"     ]

# =========================================================

def move(turtle):
    print(turtle.name)
    turtle.forward(20)
    
for turtleNo, turtle in enumerate(Turtles):
    turtle.shape("turtle")
    turtle.penup()
    turtle.setposition(StartPositions[turtleNo])
    turtle.name=Names[turtleNo]
    turtle.color(Colors[turtleNo])
    exec(f'def onKey_{Keys[turtleNo]}(): move(Turtles[{turtleNo}])')
    exec(f'screen.onkey(onKey_{Keys[turtleNo]}, "{Keys[turtleNo]}")')
    
screen.listen()
screen.exitonclick()

在某些情况下,有意义并且有助于实现泛化,从而可以将字符串变量用于变量名称或变量名称的一部分,同时将它们用作字符串。exec()

将上述代码中的列表扩展为:

Turtles         =   [Turtle()   ,   Turtle()    ,   Turtle()    ,   Turtle()    ,   Turtle()        ]
StartPositions  =   [(200,-100) ,   (200,100)   ,   (200,-50)   ,   (200,50)    ,   (200,0)     ]
Names           =   ["turti_1"  ,   "turti_2"   ,   "turti_3"   ,   "turti_4"   ,   "turti_5"   ]
Keys            =   ["w"        ,   "i"         ,   "s"         ,   "k"         ,   "b"         ]
Colors          =   ["red"      ,   "green"     ,   "blue"      ,   "cyan"      ,   "magenta"   ]

并享受五只海龟比赛......

评论

0赞 Barmar 10/31/2023
“在解析时通过”这不是真的。 不捕获变量的当前值。lambda
0赞 Claudio 11/1/2023
@Barmar:感谢您指出这一点。我错误地认为 lambda 的行为类似于设置函数参数的默认值,但事实并非如此(我必须对其进行测试才能相信这一点)。我已经更新了我的答案,删除了不真实的陈述。
0赞 Claudio 10/31/2023 #4

您在这里面临的来自 javascript 编程的实际问题是 Python 模块,可能是为了简单起见,或者根本没有充分的理由,没有将触发键盘事件处理函数的键传递给此函数,因为它通常是所有键盘输入处理支持模块的标准。 这不是 Python 本身的问题。它仅特定于 turtle.py 模块。turtle.py

我建议你阅读 ggorlen 对另一个问题的回答

序言:是一团糟,对初学者来说并不像它可能的那样友好。API 中存在大量不一致之处,它几乎违反了 Python 禅宗的所有规则。可能是因为它是很久以前写的,从那以后没有太大变化。因此,当 turtle API 或内部的某些部分莫名其妙地奇怪或不一致,或者 IDE 自动完成变得混乱时,请不要太惊讶。

您可以自己体验修补 turtle 模块,如下面的代码所示,只更改模块中的一行(以便使按下的键可用作参数)以实现您有充分理由期望的功能:turtle.py

part of turtle.py file where one line need to be changed

# The PATCH: 
from turtle import TurtleScreenBase
def _onkeyrelease(self, fun, key):
    """Bind fun to key-release event of key.
    Canvas must have focus. See method listen
    """
    if fun is None:
        self.cv.unbind("<KeyRelease-%s>" % key, None)
    else:
        def eventfun(event):
            fun(key) # <<<=== CHANGED ===>>> was:  fun()
        self.cv.bind("<KeyRelease-%s>" % key, eventfun)
TurtleScreenBase._onkeyrelease = _onkeyrelease

# The script utilizing the PATCH:
from turtle import Turtle, Screen

screen = Screen()
tur1 = Turtle()
tur1.shape("turtle")
tur1.shape("turtle")
tur1.penup()
tur1.setposition(x=-200, y=-100)

tur2 = Turtle()
tur2.shape("turtle")
tur2.penup()
tur2.setposition(x=200, y=100)

def onKey(key):
    if key=="i": 
        print("  i pressed. Moving tur1 .")
        tur1.forward(20)
    if key=="w":
        print("  w pressed. Moving tur2 .")
        tur2.forward(20)


screen.listen()

screen.onkey(onKey, "w") # how to tell that this is to be called for tur1
screen.onkey(onKey, "i") # how to tell that this is to be called for tur2

screen.exitonclick()