【问题标题】:Python, instance has no __call__ methodPython,实例没有 __call__ 方法
【发布时间】:2013-07-22 11:22:14
【问题描述】:

我正在 python2.7 中使用 tkinter。我想要做的就是在一个类中绘制一个画布,然后在另一个类中绘制一个将在画布周围移动一个正方形的类。但由于某种原因,我得到了

Exception in Tkinter callback
Traceback (most recent call last):
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk/Tkinter.py", line 1410, in __call__
    return self.func(*args)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk/Tkinter.py", line 495, in callit
    func(*args)
AttributeError: Animation instance has no __call__ method

据我所见,人们在覆盖某个值或名称遮盖另一个值时会出现此错误。但我看不出我可能会过度骑行。其他人能看到问题吗?

driver.py:

from Tkinter import *
import animation
class Alien(object):
    def __init__(self):
        #Set up canvas
        self.root = Tk()
        self.canvas = Canvas(self.root, width=400, height=400)
        self.canvas.pack()
        #Vars
        self.map = [[1, 0, 0, 1, 0], [0, 1, 0, 1, 0], [0, 0, 1, 0, 0], [0, 1, 1, 0, 0], [1, 0, 0, 1, 0]]
        self.x = 0
        self.y = 0
        r = 50
        land = {}
        #Draw Init
        for i, row in enumerate(self.map):
            for j, cell in enumerate(row):
                color = "black" if cell else "green"
                land[(i, j)] = self.canvas.create_rectangle(r * i, r * j , r * (i + 1), r * (j + 1),
                                             outline=color, fill=color)
        self.creature = self.canvas.create_rectangle(r * self.x, r * self.y, r * (self.x + 1), r * (self.y + 1),
                                                     outline="red", fill="red")
        self.canvas.pack(fill=BOTH, expand=1)
        #Action
        self.root.after(0, animation.Animation(self.root, self.canvas, self.creature))
        #Clost TK
        self.root.mainloop()
a = Alien()

animation.py:

from random import randrange


    class Animation():
        def __init__(self, root, canvas, creature):
            self.x = self.y = 0
            i = randrange(1, 5)
            if i == 1:
                self.y = -1
            elif i == 2:
                self.y = 1
            elif i == 3:
                self.x = -1
            elif i == 4:
                self.x = 1
            self.canvas = canvas
            self.creature = creature
            for i in range(10):
                root.after(250, self.animate())

        def animate(self):
                #root.after(250, self.animate(canvas, creature))
                """Moves creature around canvas"""
                self.canvas.move(self.creature, self.x * 50, self.y * 50)
                #self.canvas.update() no longer needed

【问题讨论】:

    标签: python tkinter attributeerror


    【解决方案1】:

    您将 Animation 实例传递给 after,然后它会尝试调用它。我怀疑你的意思是创建一个实例,然后传入它的 animate 方法,对吧?

    编辑:所以现在我回到互联网上来详细说明一下;问题出在这一行:

    self.root.after(0, animation.Animation(self.root, self.canvas, self.creature))
    

    这会创建一个Animation 实例并将其传递给after 方法,然后当它无法弄清楚如何调用它时,该方法就会崩溃。我会做类似的事情:

    animator = animation.Animation(self.root, self.canvas, self.creature)
    self.root.after(0, animator.animate) # get things going
    

    另外,正如其他地方所指出的,Animation 构造函数中的循环至少以两种方式被破坏:

    for i in range(10):
        root.after(250, self.animate())
    

    设置十个回调,所有回调都与当前时间相关 - 即四分之一秒后,您将一次触发十个回调。它们都会失败,因为您正在调用animate 方法并将其返回值(即None)传递给after,而不是传递方法本身。或者换句话说,

    after(ms, function())
    

    相同
    value = function()
    after(ms, value)
    

    这并不是你真正想要的。解决此问题的一种方法是将self.root = root 添加到构造函数中,然后执行以下操作:

    self.root.after(250, self.animate)
    

    animate 内部,四分之一秒后触发另一个呼叫。

    或者,您可以让 Tkinter 为您跟踪事情; after 方法允许您将参数传递给回调,并且您可以使用它来传递 after 函数本身(!)所以 animate 方法不必寻找它:

    def animate(self, after):
        ... do animation ...
        after(250, self.animate, after)
    

    然后通过调用 root.after 将其关闭,并传入该方法:

    root.after(0, animator.animate, root.after)
    

    【讨论】:

      【解决方案2】:

      您将Animation 实例作为self.root.after() 的回调。回调对象在给定时间后调用调用他们意味着他们的__call__() 方法被调用。你的Animation 类没有这样的方法。我建议添加一个然后执行您想要执行的任何操作。例如:

      class Animation():
          #
          # ... your stuff from above ...
          #
          def __call__(self):
              self.animate()
      

      另一种选择是将要调用的方法直接提供给after()

      self.root.after(0, animation.Animation(self.root, self.canvas, self.creature).animate)
      

      【讨论】:

        【解决方案3】:

        除了已经提到的错误,你还有

                    for i in range(10):
                        root.after(250, self.animate())
        

        在你的动画.py 中。这里同样适用:你想给 root.after() 一个可调用对象 self.animate,而不是调用这个可调用对象的结果 (self.animate())。

        () 调用前面提到的“事物”。如果您想现在调用它,请使用()。如果您不这样做(在这种情况下您也没有),请不要在此处输入()

        【讨论】:

        • 在那个位置连续开始十个afters 可能不是 OP 想要的。这将导致 250 毫秒后几乎同时发生十个动画(或彼此非常快)。他可能更希望有一个链接的after,这样每个动画都会导致下一次调用after
        • 好吧,如果我们讨论的是 after 的主题,有没有更好的方法来不断调用它而不会出现递归溢出?
        • @EasilyBaffled 将 root.after(250, self.animate()) 放在 for 循环之外,另外放入 animate 方法中。
        • after 不添加递归。它只是在稍后要做的事情列表中放置一个“提醒”并立即返回。稍后调度程序将考虑“提醒”并执行回调。因此,在该动画方法中调用 after 不会导致非锚定递归。
        • @Alfe 你是对的。但是,如果 after() 方法在每次调用时将自己添加到列表中 10 次,那么事情很快就会变得古怪,因为我们有指数行为。在一秒钟后有 10000 个电话,在另一秒后有 100000000 个(如果我没记错的话)听起来并不好笑。如果after 在每次调用中只添加一次,一切都会好起来的。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-03
        • 2012-07-04
        • 2018-02-23
        • 1970-01-01
        • 2015-04-07
        相关资源
        最近更新 更多