一般来说,(Python)项目是如何构建的? [英] Generally speaking, how are (Python) projects structured?
问题描述
在构建我的项目时,我有点不知所措.我尝试以合理的方式构建事物,但最终总是每天至少两次重组整个事物.诚然,我的项目不是很大,但我希望不必重组所有内容,只需一次性解决一些问题.
I'm a bit lost when it comes to structuring my project(s). I try to structure things in ways that make sense, but always end up restructuring the whole thing at least twice per day. Granted, my projects aren't very big, but I would love to not have to restructure everything and just settle on something for once.
我将描述我当前的程序以尝试理解事物.这是一个带有数据库后端的图形程序,用于计算帆的价格.尚未编写所有内容,但用户将能够从两个下拉菜单中选择风帆类别和型号.根据类别模型组合,程序将显示复选框和旋转框.这些复选框和旋转框在更改时从数据库中提取信息并显示选中该复选框或在旋转框中具有特定数字(例如,以平方米为单位的面积)的价格.
I'll describe my current program to try to make sense of things. It's a graphical program with a database backend for calculating the price of sails. Not everything is written yet, but the user will be able to select a sail category and model from two dropdown menus. Depending on the category-model combination, the program will present checkboxes and spinboxes. These checkboxes and spinboxes, when changed, draw information from a database and present the price of having that checkbox checked or having a certain number (e.g., area in square metres) in the spinbox.
在当前形式下,该项目如下所示:
In its current form, the project looks like this:
COPYING
README.md
SailQt.pyw (Should program be called from here ...)
sailqt/
__init__.py (This holds a __version__ string)
SailQt.pyw (... or here?)
gui/
__init__.py
MainWindow.py (This needs access to a __version__ string)
MainWindow_rc.py
OptionsWidget.py
ui_MainWindow.py
ui_OptionsWidget.py
resources/
__init__.py
database.db
generate_gui.py
MainWindow.ui
MainWindow.qrc
OptionsWidget.ui
icons/
logo.png
进一步澄清.resources
包含在 Qt Designer 中制作的所有 .ui
文件.它们是描述 GUI 的 XML 文件.可以使用终端工具将它们转换为 Python 脚本,我已将其嵌入到 generate_gui.py
中..qrc
文件也是如此.generate_gui.py
将自动生成的文件放在 gui
文件夹中,带有前缀 ui_
或后缀 _rc
.database.db
当前是空的,但最终将用于保存价格和所有内容.
To further clarify. resources
holds all .ui
files made in Qt Designer. They are XML files that describe the GUI. They can be converted to Python scripts with a terminal tool, which I've embedded into generate_gui.py
. The same goes for .qrc
files. generate_gui.py
places the autogenerated files in the gui
folder with either prefix ui_
or suffix _rc
. database.db
is currently empty, but will eventually be used to hold prices and everything.
MainWindow.py
和 OptionsWidget.py
是 Python 文件,它们包含同名对象,去掉 .py
后缀.MainWindow
将 OptionsWidget
保存在其显示表面中.两个对象都使用它们对应的 ui
和 rc
文件.
MainWindow.py
and OptionsWidget.py
are Python files that hold objects of the same name, minus the .py
suffix. MainWindow
holds OptionsWidget
in its display surface. Both objects use their corresponding ui
and rc
files.
SailQt.pyw
是制作 MainWindow
实例的文件,告诉它显示自己,然后告诉 (Py)Qt 进入它的循环并从那里.它基本上很像许多图形应用程序的 .exe
文件,因为它是一个让程序运行的小文件.
SailQt.pyw
is the file that makes a MainWindow
instance, tells it to show itself, and then tells (Py)Qt to enter its loop and take over from there. It's basically much like a .exe
file of a lot of graphical applications in that it's a small file that gets the program running.
我最初的猜测是将 SailQt.pyw
放在 sailqt
文件夹中.但随后 MainWindow.py
突然需要访问 __version__
字符串.我能弄清楚如何实现这一点的唯一方法是将 SailQt.pyw
移动到我的项目的根文件夹,并让 MainWindow.py
导入 sailqt.__version__
.但考虑到那是我第 n 次不得不在大多数文件中进行乱序和重做行以解决那个微小的乱序,我决定在这里问一下.
My initial guess was to place SailQt.pyw
inside the sailqt
folder. But then MainWindow.py
suddenly needed access to a __version__
string. The only way I could figure out how to achieve that was to move SailQt.pyw
to the root folder of my project, and to let MainWindow.py
import sailqt.__version__
. But considering that was the nth time I had to shuffle things around and redo lines in most files to account for that tiny shuffle, I decided to just ask here.
我的问题很清楚:
- 一般来说,Python 项目是如何构建的?这个 pydoc 链接 很有帮助,但这对我来说更像是一个模块而不是实际的东西由用户执行.
- 我的上述结构是否正确?
- 回答这个问题会加分,因为它有点跑题了.为什么我可以做
import os
然后做os.system("sudo rm -rf/")
之类的事情,但我不能做之类的事情导入sailqt
然后执行sailqt.gui.generate_gui.generate()
?
- How are, in general, Python projects structured? This pydoc link was helpful, but that seems more like a module to me than something that is actually executed by a user.
- Did I get the above structuring right?
- Bonus points for answering this, as it's a bit off-topic. How come I can do
import os
and then do stuff likeos.system("sudo rm -rf /")
, but I can't do stuff likeimport sailqt
and then dosailqt.gui.generate_gui.generate()
?
推荐答案
让我们先解决最后一个问题,因为就构建 Python 项目而言,这是最重要的.一旦您弄清楚如何让导入在您的项目中正常工作,剩下的事情就会变得更容易处理.
Let's deal with your last question first, because it's the most important as far as structuring python projects is concerned. Once you've sorted out how to get the imports working correctly within your project, the rest becomes much easier to deal with.
要理解的关键是当前运行脚本的目录会自动添加到sys.path
的start.因此,如果您将 main.py
脚本(您当前调用的 SailQt.pyw
)outside 放在包的顶层容器目录,它将保证包导入始终有效,无论脚本从何处执行.
The key thing to understand is that the directory of the currently running script is automatically added to the start of sys.path
. So if you put your main.py
script (what you're currently calling SailQt.pyw
) outside of your package in a top-level container directory, it will guarantee that package imports will always work, no matter where the script is executed from.
所以一个最小的起始结构可能是这样的:
So a minimal starting structure might look like this:
project/
main.py
package/
__init__.py
app.py
mainwindow.py
现在,因为 main.py
必须在顶级 python 包目录之外,它应该只包含最少量的代码(刚好足以获得程序启动).鉴于上述结构,这意味着仅此而已:
Now, because main.py
must be outside of the top-level python package directory, it should contain only a minimal amout of code (just enough to get the program started). Given the above structure, that would mean not much more than this:
if __name__ == '__main__':
import sys
from package import app
sys.exit(app.run())
app
模块将包含初始化程序和设置 gui 所需的大部分实际代码,这些代码将像这样导入:
The app
module would contain most of the actual code necessary to initialize the program and set up the gui, which would be imported like this:
from package.mainwindow import MainWindow
并且这种完全限定的导入语句形式可以在包的任何地方使用.因此,例如,使用这个稍微复杂的结构:
and this same form of fully qualified import statement can be used from anywhere with the package. So, for example, with this slightly more complicated structure:
project/
main.py
package/
__init__.py
app.py
mainwindow.py
utils.py
dialogs/
search.py
search
模块可以像这样从 utils
模块导入一个函数:
the search
module could import a function from the utils
module like this:
from package.utils import myfunc
关于访问 __version__
字符串的具体问题:对于 PyQt 程序,您可以将以下内容放在 app
模块的顶部:
On the specific issue of accessing the __version__
string: for a PyQt program, you could put the following at the top of the app
module:
QtGui.QApplication.setApplicationName('progname')
QtGui.QApplication.setApplicationVersion('0.1')
然后像这样访问名称/版本:
and then access the name/version later like this:
name = QtGui.qApp.applicationName()
version = QtGui.qApp.applicationVersion()
<小时>
您当前结构的其他问题主要与保持代码文件和资源文件之间的分离有关.
The other issues with your current structure are mainly to do with maintaining separation between code files and resource files.
首先:包树应该只包含代码文件(即python模块).资源文件属于项目目录(即包外).其次:包含从资源(例如,由 pyuic 或 pyrcc)生成的代码的文件应该放在一个单独的子包中(这也使您的版本控制工具可以轻松地排除它们).这将导致整体项目结构如下:
Firstly: the package tree should only contain code files (i.e. python modules). The resource files belong in the project directory (i.e. outside the package). Secondly: files containing code generated from resources (e.g. by pyuic or pyrcc) should probably go in a separate sub-package (this also makes it easy for your version control tool to exclude them). This would result in an overall project structure like this:
project/
db/
database.db
designer/
mainwindow.ui
icons/
logo.png
LICENSE
Makefile
resources.qrc
main.py
package/
__init__.py
app.py
mainwindow.py
ui/
__init__.py
mainwindow_ui.py
resources_rc.py
这里,Makefile
(或等效文件)负责生成ui/rc文件、编译python模块、安装/卸载程序等.程序运行时所需的资源(如作为数据库文件),需要安装在您的程序知道如何找到的标准位置(例如 Linux 上的 /usr/share/progname/database.db
之类的东西).在安装时,Makefile
还需要生成一个可执行的 bash 脚本(或等效脚本),该脚本知道您的程序在哪里以及如何启动它.也就是说,类似于:
Here, the Makefile
(or equivalent) is responsible for generating the ui/rc files, compiling the python modules, installing/uninstalling the program, etc. Resources needed by the program at runtime (such as the database file), will need to be installed in a standard location that your program knows how to find (e.g. something like /usr/share/progname/database.db
on Linux). At installation-time, the Makefile
will also need to generate an executable bash script (or equivalent) that knows where your program is and how to start it. That is, something like:
#!/bin/sh
exec 'python' '/usr/share/progname/main.py' "$@"
显然需要安装为 /usr/bin/progname
(或其他).
which would obviously need to be installed as /usr/bin/progname
(or whatever).
一开始这似乎有很多事情要处理,但当然,找到一个运行良好的项目结构的主要好处是,您可以将它重用于所有未来的项目(并开始开发自己的项目)用于设置和管理这些项目的模板和工具).
This may seem like quite a lot to deal with at first, but of course the major benefit of finding a project structure that works well, is that you can re-use it for all future projects (and start to develop your own templates and tools for setting up and managing those projects).
这篇关于一般来说,(Python)项目是如何构建的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!