传递函数作为Python中的方法参数返回

时间:2013-08-15 22:08:51

标签: python methods parameters iterable-unpacking

我正在编写一个小代码,用于在Tkinter GUI中移动两个球并执行其他一些操作。 我已经编写了一个有效的代码,但由于它使用了很多全局变量,我试图改进它。 下面我粘贴了与我的问题相关的代码部分:

can.coords需要五个参数:您想要“移动”的对象和新坐标。 moveLeft()和addThirty()的返回值都是两个项目列表。 当然,星号运算符(解压缩列表)不起作用。

如何将两个返回的函数列表中的四个项目传递给方法.coords()?

PS:我是Python新手,甚至是编程人员。

def moveo (lr, tb):
    global newX, newY
    newX = x+lr
    newY = y+tb
    return newX, newY


def moveLeft ():
    coordins = moveo (-10, 0)
    return coordins

def addThirty (func):
    i = 0
    coordinsNew  = func
    coordinsNew = list(coordinsNew)
    while i < 2:
        coordinsNew[i] = coordinsNew[i]+30
        i += 1
    return coordinsNew

Button(wind, text = 'Left', command=can.coords (oval1,(*moveLeft()),(*addThirty(moveLeft()))))

3 个答案:

答案 0 :(得分:1)

如果两个函数都返回相同的类型(列表或元组),那么只需执行:

can.coords(oval1, *(moveLeft() + addThirty(moveLeft())))

如果它们返回不同的类型(元组,列表,迭代器等):

args = list(moveLevt())
args.extend(addThirty(moveLeft()))
can.coords(oval1, *args)

答案 1 :(得分:1)

您始终可以将两个列表或两个元组合并为一个+

can.coords(oval1, *(moveLeft() + addThirty(moveLeft())))

即使您有不同类型的序列(甚至迭代器),也可以随时转换它们:

can.coords(oval1, *(moveLeft() + tuple(addThirty(moveLeft())))))

然而,你真的应该退后一步,并问为什么这首先需要一行。它滚动屏幕的右边缘,它需要足够复杂的括号,你必须考虑它来理解它,等等。为什么不这样做:

top, left = moveLeft()
bottom, right = addThirty(moveLeft())
can.coords(oval1, top, left, bottom, right)

在评论中,您说:

  

我不能这样做,因为我希望每次按下按钮时坐标都会改变。因此按钮需要:执行两个函数来修改坐标并一次将它们传递给can.coords()。

将它放在一行不会这样做,甚至可以帮助简化它。你编写它的方式,你只需要调用can.coords一次,并将结果返回值作为命令传递。那不是你想要的。您需要传递的是功能,它可以完成所有这些工作。

这意味着肯定想要将其拆分为多行。例如:

def update_coords():
    top, left = moveLeft()
    bottom, right = addThirty(moveLeft())
    can.coords(oval1, top, left, bottom, right)
Button(wind, text = 'Left', command=update_coords)

因为将它放在一行中的唯一方法是使用等效的lambdapartial,这甚至会比调用更多不可读;类似的东西:

Button(wind, text = 'Left', command=lambda: can.coords(oval1, *(moveLeft() + addThirty(moveLeft()))))

为了解释传递函数和调用函数并传递其返回值之间的区别,让我们看一个更简单的例子:

>>> def foo():
...     return 2
>>> print(foo)
<function foo at 0x12345678>
>>> print(foo())
2

在这里,应该很清楚区别的是什么。 foo是一个表达式,其值为函数foo本身。但foo()是一个表达式,其值通过调用foo而不带参数来确定,然后使用返回的任何内容(在本例中为2)。

如果我们让它变得更复杂一点,那就没有什么不同了:

>>> def bar(x, y):
...     return x+y
>>> print(bar)
<function bar at 0x12345680>
>>> print(bar(2, 3))
6

所以,很明显你如何传递bar本身,或者如何传递6bar(2, 3)回来......但是如果你想传递一个函数怎么办呢?可以不带参数调用并返回bar(2, 3)将返回的相同内容吗?好吧,你没有这样的事情;你必须创造它。

您可以通过两种方式执行此操作:创建新功能:

>>> def new_function():
...     return bar(2, 3)

...或部分评估功能:

>>> new_function = partial(bar, 2, 3)

你的情况增加了一些额外的皱纹:你开始使用绑定方法而不是函数,你需要确保每次运行新函数时都会对参数进行求值(因为每次调用moveLeft()两次时间而不是一次就像每次调用can.coords一样重要,而且你有一堆复杂的参数。但这些皱纹都没有让事情变得更难;你只需要看看它们:

>>> def new_function():
...     can.coords(oval1, *(moveLeft() + addThirty(moveLeft())))

(部分编写起来要困难得多,因为你必须将一系列函数组合在一起才能获得参数,你需要将它们分开...但是只要在Python中部分不是微不足道的,不要试着想出来,只需写一个显式函数。)

答案 2 :(得分:0)

很抱歉挖掘这个话题,但是在用户对另一个主题做出有趣回答之后,我想我会改进这个问题的答案。实际上,只要返回一个函数,就可以为命令分配一个带参数的函数。在这种情况下,它将避免您遇到很多麻烦,因为您不需要为每个左右,下等等编写新函数。

如您所见,我可以使用我赋给命令的函数的参数:

command=move1(0,10)

我只编写了一个椭圆形的代码,只是为了说明它是如何工作的。

from tkinter import *

x1, y1 = 135, 135
x2, y2 = 170, 170



def move1 (x, y):
    def moveo1 ():
        global x1, y1
        x1, y1 = x1+x, y1+y
        can.coords (oval1, x1, y1, x1+30, y1+30)
    return moveo1



##########MAIN############

wind = Tk()
wind.title ("Move Da Ball")

can = Canvas (wind, width = 300, height = 300, bg = "light blue")
can.pack (side = LEFT,padx = 5, pady = 5)


oval1 = can.create_oval(x1,y1,x1+30,y1+30,width=2,fill='orange') #Planet 1
Button(wind, text = 'Left', command=move1(-10,0)).pack(padx = 5, pady = 5)
Button(wind, text = 'Right', command=move1(10,0)).pack(padx = 5, pady = 5)
Button(wind, text = 'Top', command=move1(0,-10)).pack(padx = 5, pady = 5)
Button(wind, text = 'Bottom', command=move1(0,10)).pack(padx = 5, pady = 5)


Button(wind, text = 'Quit', command=wind.destroy).pack(padx = 5, pady = 5)

wind.mainloop()