Python 在Jython中使用getattr时出现堆栈溢出错误

Python 在Jython中使用getattr时出现堆栈溢出错误,python,model-view-controller,jbutton,jython,getattr,Python,Model View Controller,Jbutton,Jython,Getattr,我在Jython写一个文本编辑器。此文本编辑器有一个工具栏,其中显示有ToolbarView类,并由ToolbarController类处理。有些操作不能由ToolbarController单独处理,因此这些操作被委托给MainController类 为了避免重复代码,因为有许多操作是从ToolbarController委托给MainController的,我使用了getattr,正如我在前面的问题中所建议的那样。我也意识到我可以在工具栏视图中使用相同的机制来操作按钮,但是我无法让它工作,结果我

我在Jython写一个文本编辑器。此文本编辑器有一个工具栏,其中显示有
ToolbarView
类,并由
ToolbarController
类处理。有些操作不能由
ToolbarController
单独处理,因此这些操作被委托给
MainController

为了避免重复代码,因为有许多操作是从
ToolbarController
委托给MainController的,我使用了getattr,正如我在前面的问题中所建议的那样。我也意识到我可以在
工具栏视图中使用相同的机制来操作按钮,但是我无法让它工作,结果我得到了一个无限循环和一个
Java StackOverflowerError

这是相关代码的摘录:

工具栏视图类:

from javax.swing import JToolBar, ImageIcon, JButton

class ToolbarView(JToolBar):

    def __init__(self, controller):

        #Give reference to controller to delegate action response
        self.controller = controller

        options= ['NewFile', 'OpenFile', 'SaveFile', 'CloseFile']
        for option in options:
            methods[option] = "on" + option + "Click"
            print methods[option]

        for name, method in methods.items():
            button = JButton(name, actionPerformed=getattr(self, method))
            self.add(button)

    def __getattr__(self, name):
        return getattr(self.controller, name)
from .ToolbarView import ToolbarView
class ToolbarController(object):

    def __init__(self, mainController):

        #Create view with a reference to its controller to handle events
        self.view = ToolbarView(self)

        #Will also need delegating to parent presenter
        self.mainController = mainController

    def __getattr__(self, name):
        return getattr(self.mainController, name)
from .ToolbarController import ToolbarController

class MainController(object):

    def __init__(self):
        self.toolbarController = ToolbarController(self)

    def onNewFileClick(self, event):
        print("MainController: Creating new file...")

    def onEditFileClick(self, event):
        print("MainController: Editting new file...")

    def onSaveFileClick(self, event):
        print("MainController: Saving new file...")

    def onCloseFileClick(self, event):
        print("MainController: Closing new file...")
工具栏控制器类:

from javax.swing import JToolBar, ImageIcon, JButton

class ToolbarView(JToolBar):

    def __init__(self, controller):

        #Give reference to controller to delegate action response
        self.controller = controller

        options= ['NewFile', 'OpenFile', 'SaveFile', 'CloseFile']
        for option in options:
            methods[option] = "on" + option + "Click"
            print methods[option]

        for name, method in methods.items():
            button = JButton(name, actionPerformed=getattr(self, method))
            self.add(button)

    def __getattr__(self, name):
        return getattr(self.controller, name)
from .ToolbarView import ToolbarView
class ToolbarController(object):

    def __init__(self, mainController):

        #Create view with a reference to its controller to handle events
        self.view = ToolbarView(self)

        #Will also need delegating to parent presenter
        self.mainController = mainController

    def __getattr__(self, name):
        return getattr(self.mainController, name)
from .ToolbarController import ToolbarController

class MainController(object):

    def __init__(self):
        self.toolbarController = ToolbarController(self)

    def onNewFileClick(self, event):
        print("MainController: Creating new file...")

    def onEditFileClick(self, event):
        print("MainController: Editting new file...")

    def onSaveFileClick(self, event):
        print("MainController: Saving new file...")

    def onCloseFileClick(self, event):
        print("MainController: Closing new file...")
主控制器等级:

from javax.swing import JToolBar, ImageIcon, JButton

class ToolbarView(JToolBar):

    def __init__(self, controller):

        #Give reference to controller to delegate action response
        self.controller = controller

        options= ['NewFile', 'OpenFile', 'SaveFile', 'CloseFile']
        for option in options:
            methods[option] = "on" + option + "Click"
            print methods[option]

        for name, method in methods.items():
            button = JButton(name, actionPerformed=getattr(self, method))
            self.add(button)

    def __getattr__(self, name):
        return getattr(self.controller, name)
from .ToolbarView import ToolbarView
class ToolbarController(object):

    def __init__(self, mainController):

        #Create view with a reference to its controller to handle events
        self.view = ToolbarView(self)

        #Will also need delegating to parent presenter
        self.mainController = mainController

    def __getattr__(self, name):
        return getattr(self.mainController, name)
from .ToolbarController import ToolbarController

class MainController(object):

    def __init__(self):
        self.toolbarController = ToolbarController(self)

    def onNewFileClick(self, event):
        print("MainController: Creating new file...")

    def onEditFileClick(self, event):
        print("MainController: Editting new file...")

    def onSaveFileClick(self, event):
        print("MainController: Saving new file...")

    def onCloseFileClick(self, event):
        print("MainController: Closing new file...")
所以我期望的是,当我单击按钮时,
MainController.onNewFileClick
将被执行并在控制台中打印出该消息。如果我想从
ToolbarView
委派到
ToolbarController
,它可以工作,但当我将委派从
ToolbarController
传递到主控制器时,它不起作用。它似乎在一个无限循环中调用自己。我得到的错误是:

Traceback (most recent call last):
  File "main.py", line 3, in <module>
    MainController()
  File "/home/training/Jython/controller/MainController", line 8, in __init__
    self.toolbarController = ToolbarController(self)
  File "/home/Jython/controller/ToolbarController.py", line 8, in __init__
    self.view = ToolbarView(self)
  File "/home/Jython/controller/ToolbarView.py", line 44, in __init__
    button = JButton(name, actionPerformed=getattr(self, method))
  File "/home/Jython/controller/ToolbarView.py", line 54, in __getattr__
    return getattr(self.controller, name)
  File "/home/Jython/controller/ToolbarController.py", line 15, in __getattr__
    return getattr(self.mainController, name)
  File "/home/Jython/controller/ToolbarController.py", line 15, in __getattr__
    return getattr(self.mainController, name)

[...]

  File "/home/Jython/controller/ToolbarController.py", line 15, in __getattr__
    return getattr(self.mainController, name)
RuntimeError: maximum recursion depth exceeded (Java StackOverflowError)
回溯(最近一次呼叫最后一次):
文件“main.py”,第3行,在
主控制器()
文件“/home/training/Jython/controller/MainController”,第8行,在__
self.toolbarController=工具栏控制器(self)
文件“/home/Jython/controller/ToolbarController.py”,第8行,在__
self.view=工具栏视图(self)
文件“/home/Jython/controller/ToolbarView.py”,第44行,在__
button=JButton(名称,actionPerformed=getattr(self,method))
文件“/home/Jython/controller/ToolbarView.py”,第54行,在__
返回getattr(self.controller,name)
文件“/home/Jython/controller/ToolbarController.py”,第15行,在__
返回getattr(self.mainController,名称)
文件“/home/Jython/controller/ToolbarController.py”,第15行,在__
返回getattr(self.mainController,名称)
[...]
文件“/home/Jython/controller/ToolbarController.py”,第15行,在__
返回getattr(self.mainController,名称)
运行时错误:超过最大递归深度(Java StackOverflowerError)

我做错了什么?我在python中也尝试过类似的方法(从一个类到另一个类的委托),如果在getattr之后放置一个put
()
,它就会起作用,但是在这里我会感到困惑,因为JButton中执行了
操作。我试过了,但结果都一样

你似乎在使用Jython,我真的不知道。无论如何,在python中,您重写了
\uuu getattr\uuu
,然后您应该期望
getattr
使用被重写的钩子。所以我想你的意思是:

class ToolbarView(JToolBar):

    def __init__(self, controller):

        #Give reference to controller to delegate action response
        self.controller = controller

        options= ['NewFile', 'OpenFile', 'SaveFile', 'CloseFile']
        for option in options:
            methods[option] = "on" + option + "Click"
            print methods[option]

        for name, method in methods.items():
            button = JButton(name, actionPerformed=super(ToolbarView, self).__getattr__(method))
            self.add(button)

    def __getattr__(self, name):
        return getattr(self.controller, name)
观察按钮是如何创建的

至于为什么会有这样的问题,这是因为
getattr
是如何处理的。如果覆盖
\uuuu getattr\uuuu
,则只有在尝试引用未定义的字段时,才会调用此钩子:

>>> class A(object):
    defined = True
    def __getattr__(self, name):
        print "referenced :" + name


>>> a = A()
>>> a.defined
True
>>> a.undefined
referenced :undefined
希望现在能弄清楚钩子是怎么工作的

因此,so实际上是由于您引用了不属于
MainController
的内容造成的

在您的
MainController
中,仅定义了
onNewFileClick
,但您定义了3个其他选项:

options= ['NewFile', 'OpenFile', 'SaveFile', 'CloseFile']

因此,这将发生在第二轮迭代中。由于
main控制器
没有打开文件单击
,将引发一个
属性错误
,但被
ToolbarController
捕获,因此被覆盖的
\uu getattr\uuuu
将被调用并继续。这就是你的调用堆栈爆炸的原因。

我把这归咎于
getattr
,因为我还没有信心使用它,但事实证明它是相当基本的东西

在创建工具栏视图之后,我将
main控制器
分配给
ToolbarController
,该视图随后调用
ToolbarView.\uuuu getatrr\uuuuuuuu
,该视图调用
ToolbarController.\uu getattr\uuuuuuuuuu
,试图访问尚不存在的
self.mainController

这是我需要在
ToolbarController
类中进行的更改

之前

from .ToolbarView import ToolbarView
class ToolbarController(object):

    def __init__(self, mainController):

        #Create view with a reference to its controller to handle events
        self.view = ToolbarView(self)

        #Will also need delegating to parent presenter
        self.mainController = mainController

    def __getattr__(self, name):
        return getattr(self.mainController, name)
之后:

from .ToolbarView import ToolbarView
class ToolbarController(object):

    def __init__(self, mainController):

        #Needs to delegate to main presenter. 
        #Note self.mainController needs to exist before creating the ToolbarView
        #since it needs delegating actions to it!
        self.mainController = mainController

        #Create view with a reference to its controller to handle events
        self.view = ToolbarView(self)

    def __getattr__(self, name):
        return getattr(self.mainController, name)

非常感谢@hustmphrr和@ArtOfWarfare的帮助。

这与您的主控制器无关。我想看看
actionPerformed=getattr(self,method)
,什么是
self.method
?例如,为什么需要使用
getattr
而不是
运算符?我想知道两件事#1-为什么您没有为
MainController
ToolbarController
指定基类?我在Jython中根本没有做过OOP,只做过函数式编程,但我的理解是Jython一直停留在版本2,这意味着您的对象应该继承自
object
,而不是什么都不继承,如果您想依赖新样式的类行为(我认为
getattr
是其中的一部分)#2-你的课程真的和你想象的一样吗?调试-在
ToolbarController.\uuu getattr\uuu
中,打印出
self.mainController
的类。我猜这是一个
ToolbarController
实例。谢谢你的提示,@ArtOfWarfare#1 Jython仍在版本2.X中。我现在更改了我的类,它们从对象继承。我也会用这个来编辑我的问题#2.很有趣。我在尝试访问
self.mainController.\uuuuu class\uuuuuu.\uuuuuuu name\uuuuuuuuu
时遇到堆栈溢出错误,因此引起问题的不是getattr,似乎是对mainController的引用:OI'm Study!创建后,我将
mainController
分配给
ToolbarController