m3.b
m3.b

Reputation: 667

Using the turtle package, is it possible to create a function asking for an argument? (And then use it for the .onkeypress method)

I tried myself at making the Pong project using the turtle package on Python.

I know how to make the code work (using 4 different functions taking no arguments, for each paddle and directions), but my question is if and how can I do it with this idea.

Here's the code I'm attempting to make work :

def move_paddle_up(paddle):
        y = paddle.ycor()
        y += 20
        paddle.sety(y)
    
def move_paddle_down(paddle):
        y = paddle.ycor()
        y -= 20
        paddle.sety(y)

#Keyboard binding movement
screen.listen()
screen.onkeypress(move_paddle_up(left_pad), "w")
screen.onkeypress(move_paddle_down(left_pad), "s")
screen.onkeypress(move_paddle_up(right_pad), "Up")
screen.onkeypress(move_paddle_down(right_pad), "Down")

When the screen is launched, the paddles won't move if I press the associated keys.

With the way of using the 4 different functions, it works.

It's just that I am curious to know how I should call or define the arguments in such a function.

Upvotes: 1

Views: 1085

Answers (2)

cdlane
cdlane

Reputation: 41905

I see then that is it quite more work than just creating 4 functions.

I think you got the wrong take home message. @BehdadAbdollahiMoghadam's use of functools.partial() is spot on, but we can fit it into your existing code with less overhead:

from functools import partial

# ...

def move_paddle_up(paddle):
    paddle.sety(paddle.ycor() + 20)

def move_paddle_down(paddle):
    paddle.sety(paddle.ycor() - 20)

# Keyboard binding movement
screen.onkeypress(partial(move_paddle_up, left_pad), "w")
screen.onkeypress(partial(move_paddle_down, left_pad), "s")
screen.onkeypress(partial(move_paddle_up, right_pad), "Up")
screen.onkeypress(partial(move_paddle_down, right_pad), "Down")
screen.listen()

# ...

Upvotes: 1

The onkeypress method doesn't allow to pass a function with arguments, however you can use functools.partial for this kind of situations.

You can create a function which handles all type of keyboard presses, and pass that function as a partial function.
Let's create that function first:

def move_paddle(paddle, key_pressed):

    def move_paddle_up(paddle):
            y = paddle.ycor()
            y += 20
            paddle.sety(y)
        
    def move_paddle_down(paddle):
            y = paddle.ycor()
            y -= 20
            paddle.sety(y)

    if paddle == left_pad:
       if key_pressed == "w":
          move_paddle_up(paddle)
       elif key_pressed == "s":
          move_paddle_down(paddle)

    elif paddle == right_pad:
       if key_pressed == "Up":
          move_paddle_up(paddle)
       elif key_pressed == "Down":
          move_paddle_down(paddle)

Now we can use this function with functools.partial:

import functools
[...]
screen.onkeypress(functools.partial(move_paddle,[left_pad,"w"]), key="w")
screen.onkeypress(functools.partial(move_paddle,[left_pad,"s"]), key="s")
screen.onkeypress(functools.partial(move_paddle,[right_pad,"Up"]), key="Up")
screen.onkeypress(functools.partial(move_paddle,[right_pad,"Down"]), key="Down")

The way functools.partial works:
It takes a function with some arguments (*args, it's actually an unpacked list), and then creates a new function which is the previous function that gets those args as input:

def partial(func, /, *args, **keywords):
    def newfunc(*fargs, **fkeywords):
        newkeywords = {**keywords, **fkeywords}
        return func(*args, *fargs, **newkeywords)
    newfunc.func = func
    newfunc.args = args
    newfunc.keywords = keywords
    return newfunc

Upvotes: 1

Related Questions