在 Jython 中使用 getattr 时出现 StackOverflowError
StackOverflowError when using getattr in Jython
我正在用 Jython 编写一个文本编辑器。这个文本编辑器有一个工具栏,它用 ToolbarView
class 显示并由 ToolbarController
class 处理。 ToolbarController
自己无法处理某些操作,因此将这些操作委托给 MainController
class.
为了避免重复代码,因为有许多操作从 ToolbarController
委派给了 MainController,我使用了 getattr,正如我在上一个问题 中所建议的那样。我还意识到我可以在 ToolbarView
代码中使用相同的机制来实现按钮的操作,但我无法让它工作,我最终得到一个无限循环和 Java WhosebugError
。
这是相关代码的摘录:
工具栏视图 class:
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)
工具栏控制器 class:
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)
主控制器 class:
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 WhosebugError)
我做错了什么?我在 python 中尝试过类似的东西(从 class 委托到另一个 class 到另一个 class)并且如果在 getattr 之后放置 ()
它会起作用,但在这里我因为 JButton 中的 actionPerformed
而感到困惑。我试过了,但结果是一样的。
您似乎在使用 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__
被调用并不断调用。这就是你的调用堆栈爆炸的原因。
我把这归咎于 getattr
因为我还没有那么自信地使用它,但事实证明它是相当基本的东西。
我正在将 mainController
分配给 ToolbarController
AFTER 创建 ToolbarView 然后调用 ToolbarView.__getatrr__
,它调用 ToolbarController.__getattr__
试图访问尚不存在的 self.mainController
!
这是我需要在 ToolbarController
class 中进行的更改。
之前
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 的帮助。
我正在用 Jython 编写一个文本编辑器。这个文本编辑器有一个工具栏,它用 ToolbarView
class 显示并由 ToolbarController
class 处理。 ToolbarController
自己无法处理某些操作,因此将这些操作委托给 MainController
class.
为了避免重复代码,因为有许多操作从 ToolbarController
委派给了 MainController,我使用了 getattr,正如我在上一个问题 ToolbarView
代码中使用相同的机制来实现按钮的操作,但我无法让它工作,我最终得到一个无限循环和 Java WhosebugError
。
这是相关代码的摘录:
工具栏视图 class:
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)
工具栏控制器 class:
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)
主控制器 class:
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 WhosebugError)
我做错了什么?我在 python 中尝试过类似的东西(从 class 委托到另一个 class 到另一个 class)并且如果在 getattr 之后放置 ()
它会起作用,但在这里我因为 JButton 中的 actionPerformed
而感到困惑。我试过了,但结果是一样的。
您似乎在使用 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__
被调用并不断调用。这就是你的调用堆栈爆炸的原因。
我把这归咎于 getattr
因为我还没有那么自信地使用它,但事实证明它是相当基本的东西。
我正在将 mainController
分配给 ToolbarController
AFTER 创建 ToolbarView 然后调用 ToolbarView.__getatrr__
,它调用 ToolbarController.__getattr__
试图访问尚不存在的 self.mainController
!
这是我需要在 ToolbarController
class 中进行的更改。
之前
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 的帮助。