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

传递函数在Python中作为方法参数返回,python,methods,parameters,iterable-unpacking,Python,Methods,Parameters,Iterable Unpacking,我目前正在编写一个小代码,在Tkinter GUI中移动两个球,并做一些其他事情。 我已经编写了一个有效的代码,但由于它使用了很多全局变量,我试图对其进行改进。 下面我粘贴了与我的问题相关的代码部分: can.coords需要五个参数:要“移动”的对象和新坐标。 moveLeft()和AddThreet()的返回都是两个项目列表。 当然,星形操作符(打开列表)不起作用 如何将两个返回的函数列表中的四项传递到方法.coords() PS:我对Python甚至编程都是新手 def moveo (lr

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

can.coords需要五个参数:要“移动”的对象和新坐标。 moveLeft()和AddThreet()的返回都是两个项目列表。 当然,星形操作符(打开列表)不起作用

如何将两个返回的函数列表中的四项传递到方法.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()))))
def移动(lr,tb): 全球新x,新y newX=x+lr newY=y+tb 返回newX,newY def moveLeft(): 坐标=moveo(-10,0) 返回坐标 def ADD30(功能): i=0 coordinsNew=func coordinsNew=列表(coordinsNew) 而i<2: coordinsNew[i]=coordinsNew[i]+30 i+=1 返回坐标新 按钮(wind,text='Left',command=can.coords(oval1,(*moveLeft()),(*addthree(moveLeft())))
如果两个函数返回相同的类型(列表或元组),则只需执行以下操作:

can.coords(oval1, *(moveLeft() + addThirty(moveLeft())))
如果它们返回不同的类型(元组、列表、迭代器等),请执行以下操作:


只要使用
+
,您就可以将两个列表或两个元组合并为一个:

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)
>>> def foo():
...     return 2
>>> print(foo)
<function foo at 0x12345678>
>>> print(foo())
2
因为将其放在一行的唯一方法是使用等效的
lambda
partial
,这将比调用更难读取;比如:

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

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

def update_coords():
    top, left = moveLeft()
    bottom, right = addThirty(moveLeft())
    can.coords(oval1, top, left, bottom, right)
Button(wind, text = 'Left', command=update_coords)
>>> def foo():
...     return 2
>>> print(foo)
<function foo at 0x12345678>
>>> print(foo())
2
因此,很明显,你如何传递
bar
本身,或者如何传递从
bar(2,3)
返回的
6
…但是如果你想传递一个可以无参数调用的函数,并返回与
bar(2,3)
返回的相同的东西,该怎么办?嗯,你没有这种东西;你必须创造它

可以通过两种方式执行此操作:创建新函数:

>>> def new_function():
...     return bar(2, 3)
>>> new_function = partial(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中不是微不足道的时候,不要试图去弄清楚它,只需编写一个显式函数即可。)

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

如您所见,我可以将参数用于分配给命令的函数:

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

在他的示例中,两个函数似乎都不返回列表。幸运的是,它们都返回一个元组,因此您可以将它们与
+
连接起来。另外,为什么显式的
args=list(…)
然后
args.extend(b)
而不仅仅是
list(a)+list(b)
?实际上我的代码不起作用,因为我忘记了将返回从moveLeft()转换为list。当然,您编写代码时只需解包一个星号的方式更好。谢谢。@PierreOcinom:但是如果它们都是元组(假设你可以确定这总是正确的),你不需要将它们中的任何一个转换成一个列表。顺便说一句:即使你只有一个iterable参数需要解包,或者如果Python的未来版本允许多个参数(有人建议,如果有人能够解决所有边缘情况,可能会发生这种情况……假设有一种方法可以解决这些问题……),您不能将带星号的表达式括在括号中。这迫使您在调用函数之前将其作为单独的表达式进行计算,这将引发类似于
SyntaxError的问题:只能将带星号的表达式用作赋值目标(在不同的Python版本中略有不同)。谢谢你的提醒。我之前有过这个错误。但是,现在,
*moveLeft()+*addthree(moveLeft())
*(moveLeft())+*(addthree(moveLeft())
都出现了一个“无效语法”错误。为什么