我正在Jython中编写一个文本编辑器。此文本编辑器有一个工具栏,显示为ToolbarView
类,由ToolbarController
类处理。 ToolbarController
本身无法处理某些操作,因此会将这些操作委托给MainController
类。
为了避免重复代码,因为有很多操作从ToolbarController
委托给MainController,我使用了getattr,就像我之前提出的问题here中所建议的那样。我也意识到我可以在ToolbarView
代码中使用相同的机制来处理按钮的操作,但我无法使其工作,最终得到一个无限循环和Java StackOverflowError
。
这是相关代码的摘录:
工具栏视图类:
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)
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)
MainController 类:
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
传递给MainController时,它不起作用。它似乎把自己称为无限循环。我得到的错误是:
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)
我做错了什么?我在python中尝试了类似的东西(从一个类委托给另一个类到另一个类)并且如果在getattr之后放置()
它会起作用,但是在这里我因为actionPerformed
而感到困惑JButton的。我试过了,但结果是一样的。
答案 0 :(得分:1)
似乎你正在使用Jython
,我真的不知道。无论如何,在python中,你重写__getattr__
,那么你应该期望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)
观察按钮的创建方式。
就您遇到SO问题的原因而言,这是因为getattr
的处理方式。如果覆盖__getattr__
,只有在尝试引用未定义的字段时才会调用此挂钩:
>>> 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']
所以,这将在第二轮迭代中发生。由于MainController
没有onOpenFileClick
,因此AttributeError
会被引发,但会被ToolbarController
捕获,因此会一直调用被覆盖的__getattr__
。这就是你的调用栈爆炸的原因。
答案 1 :(得分:1)
我把这归咎于getattr
,因为我还没有那么自信地使用它,但事实证明它是相当基本的东西。
我将mainController
分配给ToolbarController
AFTER ,创建工具栏视图,然后调用ToolbarView.__getatrr__
,调用ToolbarController.__getattr__
尝试访问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)
非常感谢@HuStmpHrrr和@ArtOfWarfare的帮助。