Python turtle/tkinter 事件中的部分 vs lambda
Partial vs lambda in Python turtle/tkinter events
在 Python turtle 中,如果我想传递与事件系统指定的不同的事件处理程序参数,我可以使用 lambda
弥合差异:
from turtle import Screen, Turtle
from functools import partial
def change_color(color, x=None, y=None):
screen.bgcolor(color)
screen = Screen()
screen.onclick(lambda x, y: change_color('blue'))
screen.mainloop()
或者我可以使用从 functools 导入的 partial
函数将 lambda
替换为:
screen.onclick(partial(change_color, 'blue'))
这很有效很好。回到我们原来的程序,我们可以用 ontimer()
事件替换我们的 onclick()
事件,更新我们的 lambda
,并且一切正常 fine:
screen.ontimer(lambda: change_color('blue'), 1000)
但是,当我们将 lambda
替换为 partial
时:
screen.ontimer(partial(change_color, 'blue'), 1000)
它 立即失败(不是在计时器启动时):
Traceback (most recent call last):
File "test.py", line 9, in <module>
screen.ontimer(partial(change_color, 'blue'), 1000)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/turtle.py", line 1459, in ontimer
self._ontimer(fun, t)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/turtle.py", line 718, in _ontimer
self.cv.after(t, fun)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/tkinter/__init__.py", line 755, in after
callit.__name__ = func.__name__
AttributeError: 'functools.partial' object has no attribute '__name__'
>
由于 turtle 位于 tkinter 之上,并且 tkinter 涉及堆栈跟踪,我们可以下一层:
import tkinter as tk
from functools import partial
def change_color(color):
root.configure(bg=color)
root = tk.Tk()
root.after(1000, change_color, 'blue')
root.mainloop()
效果很好很好。我们还可以:
root.after(1000, lambda: change_color('blue'))
效果很好很好。但是当我们这样做时:
root.after(1000, partial(change_color, 'blue'))
立即再次失败:
Traceback (most recent call last):
File "test.py", line 9, in <module>
root.after(1000, partial(change_color, 'blue'))
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/tkinter/__init__.py", line 755, in after
callit.__name__ = func.__name__
AttributeError: 'functools.partial' object has no attribute '__name__'
>
partial
的 functools 文档声明其 return 值的行为类似于 function 但显然它是不同的,如果不是缺乏,不知何故。这是为什么?为什么 tkinter/turtle 接受 partial
函数作为 click 事件处理程序,而不是 timer 事件处理程序?
从 here 看来,functools.partial
没有从内部函数复制 __module__
和 __name__
属性。您可以通过手动定义 __name__
来解决它:
import tkinter as tk
from functools import partial
def change_color(color):
root.configure(bg=color)
root = tk.Tk()
c = partial(change_color, 'blue')
c.__name__ = "c"
root.after(1000, c)
root.mainloop()
Why is that?
就是这样设计的。
默认情况下,partial
stores 没有属性的打包函数,但是它们仍然可用:
partial_func = partial(change_color, 'blue')
print(partial_func.func.__name__)
您应该使用 update_wrapper
function (or a decorator counterpart) 来明确设置正确的选项:
from turtle import Screen, Turtle
from functools import partial, update_wrapper
def change_color(color, x=None, y=None):
screen.bgcolor(color)
def partial_change_color(color):
partial_f = partial(change_color, color)
update_wrapper(partial_f, change_color)
return partial_f
screen = Screen()
screen.ontimer(partial_change_color('blue'), 1000)
screen.mainloop()
And why does tkinter/turtle accept partial functions as click event handers, but not as timer event handlers?
同样,它是这样设计的。
因为绑定和调度算法在 tkinter 包装器中略有不同,can be observed if you track down your error.
tkinter
创建额外的包装器 callit
,它使用 __name__
(因此,AttributeError
)处理目标函数的取消调度,而绑定 does not have such an algorithm for implicit unbinding .
在 Python turtle 中,如果我想传递与事件系统指定的不同的事件处理程序参数,我可以使用 lambda
弥合差异:
from turtle import Screen, Turtle
from functools import partial
def change_color(color, x=None, y=None):
screen.bgcolor(color)
screen = Screen()
screen.onclick(lambda x, y: change_color('blue'))
screen.mainloop()
或者我可以使用从 functools 导入的 partial
函数将 lambda
替换为:
screen.onclick(partial(change_color, 'blue'))
这很有效很好。回到我们原来的程序,我们可以用 ontimer()
事件替换我们的 onclick()
事件,更新我们的 lambda
,并且一切正常 fine:
screen.ontimer(lambda: change_color('blue'), 1000)
但是,当我们将 lambda
替换为 partial
时:
screen.ontimer(partial(change_color, 'blue'), 1000)
它 立即失败(不是在计时器启动时):
Traceback (most recent call last):
File "test.py", line 9, in <module>
screen.ontimer(partial(change_color, 'blue'), 1000)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/turtle.py", line 1459, in ontimer
self._ontimer(fun, t)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/turtle.py", line 718, in _ontimer
self.cv.after(t, fun)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/tkinter/__init__.py", line 755, in after
callit.__name__ = func.__name__
AttributeError: 'functools.partial' object has no attribute '__name__'
>
由于 turtle 位于 tkinter 之上,并且 tkinter 涉及堆栈跟踪,我们可以下一层:
import tkinter as tk
from functools import partial
def change_color(color):
root.configure(bg=color)
root = tk.Tk()
root.after(1000, change_color, 'blue')
root.mainloop()
效果很好很好。我们还可以:
root.after(1000, lambda: change_color('blue'))
效果很好很好。但是当我们这样做时:
root.after(1000, partial(change_color, 'blue'))
立即再次失败:
Traceback (most recent call last):
File "test.py", line 9, in <module>
root.after(1000, partial(change_color, 'blue'))
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/tkinter/__init__.py", line 755, in after
callit.__name__ = func.__name__
AttributeError: 'functools.partial' object has no attribute '__name__'
>
partial
的 functools 文档声明其 return 值的行为类似于 function 但显然它是不同的,如果不是缺乏,不知何故。这是为什么?为什么 tkinter/turtle 接受 partial
函数作为 click 事件处理程序,而不是 timer 事件处理程序?
从 here 看来,functools.partial
没有从内部函数复制 __module__
和 __name__
属性。您可以通过手动定义 __name__
来解决它:
import tkinter as tk
from functools import partial
def change_color(color):
root.configure(bg=color)
root = tk.Tk()
c = partial(change_color, 'blue')
c.__name__ = "c"
root.after(1000, c)
root.mainloop()
Why is that?
就是这样设计的。
默认情况下,partial
stores 没有属性的打包函数,但是它们仍然可用:
partial_func = partial(change_color, 'blue')
print(partial_func.func.__name__)
您应该使用 update_wrapper
function (or a decorator counterpart) 来明确设置正确的选项:
from turtle import Screen, Turtle
from functools import partial, update_wrapper
def change_color(color, x=None, y=None):
screen.bgcolor(color)
def partial_change_color(color):
partial_f = partial(change_color, color)
update_wrapper(partial_f, change_color)
return partial_f
screen = Screen()
screen.ontimer(partial_change_color('blue'), 1000)
screen.mainloop()
And why does tkinter/turtle accept partial functions as click event handers, but not as timer event handlers?
同样,它是这样设计的。 因为绑定和调度算法在 tkinter 包装器中略有不同,can be observed if you track down your error.
tkinter
创建额外的包装器 callit
,它使用 __name__
(因此,AttributeError
)处理目标函数的取消调度,而绑定 does not have such an algorithm for implicit unbinding .