【问题标题】:Partial vs lambda in Python turtle/tkinter eventsPython turtle/tkinter 事件中的部分 vs lambda
【发布时间】:2019-11-24 07:08:53
【问题描述】:

在 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,一切正常很好

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__'
>

partialfunctools 文档声明其返回值的行为类似于 函数,但显然它是不同的,如果不是缺少的话,不知何故。这是为什么?为什么 tkinter/turtle 接受 partial 函数作为 click 事件处理程序,而不是作为 timer 事件处理程序?

【问题讨论】:

    标签: python tkinter event-handling turtle-graphics


    【解决方案1】:

    这是为什么呢?

    它是这样设计的。 默认情况下,partialstores 是没有属性的打包函数,但是它们仍然可用:

    partial_func = partial(change_color, 'blue')
    print(partial_func.func.__name__)
    

    您应该使用update_wrapper 函数(或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()
    

    为什么 tkinter/turtle 接受部分函数作为点击事件处理程序,而不是作为计时器事件处理程序?

    同样,它是这样设计的。 因为绑定和调度算法在 tkinter 包装器中略有不同,can be observed if you track down your error

    tkinter 创建额外的包装器callit,它使用__name__(因此,AttributeError)处理目标函数的取消调度,而绑定does not have such an algorithm for implicit unbinding

    【讨论】:

      【解决方案2】:

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

      【讨论】:

      • 我不需要变通——正如我演示的 lambdapartial 失败的地方工作。跟进您的回答,谢谢,我做了两件事:我为我的系统升级到了最新的 Python;我搜索了有关此错误的较早报告。它似乎在当前的 Python 中没有得到修复,关于它的报告可以追溯到十多年前。我想我的第一个问题现在更像是,“为什么还是这样?”。我的第二个问题是为什么它会影响一些 tkinter 事件处理程序,但不影响其他事件处理程序,这个问题仍未得到解答。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-12
      • 1970-01-01
      • 2017-10-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多