Python 在 init 之外定义 gui 变量
Python defining gui variables outside init
我正在使用 PySide2
来定义我的工具的界面,并且我通常会在 __init__
之外初始化所有界面项,以免使其膨胀(任何其他重要变量都保留在 __init__
中).
不幸的是,我正在使用 PyCharm 作为我的编辑器,它给了我很多警告:
Instance attribute 'foobar' defined outside __init __
这是我将要执行的操作的一个简单示例:
from PySide2 import QtWidgets
class MyTool(QtWidgets.QWidget):
def __init__(self, parent=None):
super(MyTool, self).__init__(parent)
self.create_gui()
def create_gui(self):
# Complains about all variables below!
self.awesome_checkbox = QtWidgets.QCheckBox(parent=self)
self.awesome_button = QtWidgets.QPushButton(parent=self)
self.awesome_label = QtWidgets.QLabel(parent=self)
self.main_layout = QtWidgets.QVBoxLayout()
self.main_layout.addWidget(self.awesome_checkbox)
self.main_layout.addWidget(self.awesome_button)
self.main_layout.addWidget(self.awesome_label)
self.setLayout(self.main_layout)
现在我知道一个解决方案是将 __init__
中的这些变量初始化为 None
,但我可以有相当复杂的接口,所以它会很长。
我的问题是,我目前所做的事情是否真的是在亵渎神明?我知道这些变量在技术上是在 __init__
之外的,但无论如何都会在构造函数中调用该方法!
嗯,简短的回答是:不,不是 "truly blasphemy"。
创建所有实例属性并确保它们在初始化程序中处于一致状态被认为是一种很好的做法,因为它使代码更易于阅读(您只有一种方法可以阅读以了解您的对象具有哪些属性)并且避免潜在的 AttributeError 当属性是通过在访问属性之前可能并不总是被调用的方法创建时。这就是为什么大多数 linter 会(默认情况下)就此向您发出警告,这本身就是一件好事,因为它可以帮助您在潜在错误进入生产环境之前发现它。
现在确实存在这样的情况,将部分实例初始化委托给不同的方法是有意义的,即当这些属性相互依赖以及一些其他外部因素并且可能必须在实例的生命周期,或者当您想让子 classes 覆盖初始化的这一部分而不必覆盖 __init__()
方法本身时(参见 GOF 的 "template method" 模式)。
在 class 具有复杂初始化的情况下(这是典型的 GUI 组件),出于可读性原因将设置拆分为不同的方法也是有意义的 - 50 多行初始化程序不是在可读性方面确实是最优的——所以就我而言,我可能会做一些类似的事情,可能还有一些改进:首先将其设为 "protected" 方法(将其命名为 _create_gui()
- 前导下划线作为受保护的属性/方法的命名约定),然后添加一个守卫以防止该方法被执行两次(假设该方法只应该从初始化程序中调用一次并且不应该是 [=25= 的一部分] API,当然)。然后我会添加一些 linter 指令(那些是 linter 寻找的特殊格式的注释)以向 linter 和任何阅读此代码的人表明这样做是故意的设计选择而不是新手错误。
我正在使用 PySide2
来定义我的工具的界面,并且我通常会在 __init__
之外初始化所有界面项,以免使其膨胀(任何其他重要变量都保留在 __init__
中).
不幸的是,我正在使用 PyCharm 作为我的编辑器,它给了我很多警告:
Instance attribute 'foobar' defined outside __init __
这是我将要执行的操作的一个简单示例:
from PySide2 import QtWidgets
class MyTool(QtWidgets.QWidget):
def __init__(self, parent=None):
super(MyTool, self).__init__(parent)
self.create_gui()
def create_gui(self):
# Complains about all variables below!
self.awesome_checkbox = QtWidgets.QCheckBox(parent=self)
self.awesome_button = QtWidgets.QPushButton(parent=self)
self.awesome_label = QtWidgets.QLabel(parent=self)
self.main_layout = QtWidgets.QVBoxLayout()
self.main_layout.addWidget(self.awesome_checkbox)
self.main_layout.addWidget(self.awesome_button)
self.main_layout.addWidget(self.awesome_label)
self.setLayout(self.main_layout)
现在我知道一个解决方案是将 __init__
中的这些变量初始化为 None
,但我可以有相当复杂的接口,所以它会很长。
我的问题是,我目前所做的事情是否真的是在亵渎神明?我知道这些变量在技术上是在 __init__
之外的,但无论如何都会在构造函数中调用该方法!
嗯,简短的回答是:不,不是 "truly blasphemy"。
创建所有实例属性并确保它们在初始化程序中处于一致状态被认为是一种很好的做法,因为它使代码更易于阅读(您只有一种方法可以阅读以了解您的对象具有哪些属性)并且避免潜在的 AttributeError 当属性是通过在访问属性之前可能并不总是被调用的方法创建时。这就是为什么大多数 linter 会(默认情况下)就此向您发出警告,这本身就是一件好事,因为它可以帮助您在潜在错误进入生产环境之前发现它。
现在确实存在这样的情况,将部分实例初始化委托给不同的方法是有意义的,即当这些属性相互依赖以及一些其他外部因素并且可能必须在实例的生命周期,或者当您想让子 classes 覆盖初始化的这一部分而不必覆盖 __init__()
方法本身时(参见 GOF 的 "template method" 模式)。
在 class 具有复杂初始化的情况下(这是典型的 GUI 组件),出于可读性原因将设置拆分为不同的方法也是有意义的 - 50 多行初始化程序不是在可读性方面确实是最优的——所以就我而言,我可能会做一些类似的事情,可能还有一些改进:首先将其设为 "protected" 方法(将其命名为 _create_gui()
- 前导下划线作为受保护的属性/方法的命名约定),然后添加一个守卫以防止该方法被执行两次(假设该方法只应该从初始化程序中调用一次并且不应该是 [=25= 的一部分] API,当然)。然后我会添加一些 linter 指令(那些是 linter 寻找的特殊格式的注释)以向 linter 和任何阅读此代码的人表明这样做是故意的设计选择而不是新手错误。