我正在编写一个小代码,用于在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()))))
答案 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)
因为将它放在一行中的唯一方法是使用等效的lambda
或partial
,这甚至会比调用更多不可读;类似的东西:
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
本身,或者如何传递6
从bar(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()