如何编写python模块/包(module/package)?

How to write a Python module/package?

我一直在为工作中的简单任务制作Python脚本,从来没有真正费心打包它们供其他人使用。现在我被指派为RESTAPI制作一个Python包装器。我完全不知道如何开始,我需要帮助。

我所拥有的:

(只想尽可能具体)我已经准备好了virtualenv,它也在Github中,还有用于python的.gitignore文件,另外还有用于与REST API交互的请求库。就是这样。

这是当前目录树

1
2
3
4
5
6
7
8
9
10
11
12
13
.
├── bin
│   └── /the usual stuff/
├── include
│   └── /the usual stuff/
├── lib
│   └── python2.7
│       └── /the usual stuff/
├── local
│   └── /the usual stuff/
└── README.md

27 directories, 280 files

我甚至不知道把.py文件放在哪里,如果我做过.py文件的话。

我想做的是:

使用"pip install…"使python模块可以安装。

如果可能的话,我希望有一个关于编写Python模块的一般步骤。


模块是包含Python定义和语句的文件。文件名是后缀为.py的模块名。

创建hello.py,然后编写以下函数作为其内容:

1
2
def helloworld():
   print"hello"

然后可以导入hello

1
2
3
4
>>> import hello
>>> hello.helloworld()
'hello'
>>>

将多个.py文件组合在一个文件夹中。任何带有__init__.py的文件夹都被python视为模块,您可以将它们称为包。

1
2
3
|-HelloModule
  |_ __init__.py
  |_ hellomodule.py

您可以按照通常的方式在模块上使用import语句。

有关更多信息,请参见6.4。包装。


python 3-2015年11月18日更新

发现公认的答案是有用的,但希望根据我自己的经验,为他人的利益扩展几点。

模块:模块是包含Python定义和语句的文件。文件名是附加后缀.py的模块名。

模块示例:假设当前目录中只有一个python脚本,这里我将其命名为mymodule.py。

mymodule.py文件包含以下代码:

1
2
def myfunc():
    print("Hello!")

如果我们从当前目录运行python3解释器,我们可以通过以下不同的方式导入和运行函数myfunc(您通常只需选择以下方法之一):

1
2
3
4
5
6
7
8
9
>>> import mymodule
>>> mymodule.myfunc()
Hello!
>>> from mymodule import myfunc
>>> myfunc()
Hello!
>>> from mymodule import *
>>> myfunc()
Hello!

好吧,那就简单多了。

现在假设您需要将这个模块放到它自己的专用文件夹中,以提供一个模块名称空间,而不只是从当前工作目录临时运行它。这是一个值得解释包装概念的地方。

包:包是通过使用"点式模块名称"来构造Python模块名称空间的一种方法。例如,模块名a.b在名为a的包中指定名为b的子模块。就像使用模块可以避免不同模块的作者担心彼此的全局变量名一样,使用点状模块名可以避免多模块包(如numpy或python imaging library)的作者必须担心彼此的模块名。

包示例:现在假设我们有以下文件夹和文件。在这里,mymodule.py与之前的完全相同,并且uu init_uuu.py是一个空文件:

1
2
3
4
.
└── mypackage
    ├── __init__.py
    └── mymodule.py

为了让python将目录视为包含包,需要使用u init .py文件。有关更多信息,请参阅稍后提供的模块文档链接。

我们当前的工作目录比名为mypackage的普通文件夹高一级。

1
2
$ ls
mypackage

如果现在运行python3解释器,我们可以通过以下不同的方式导入和运行包含所需函数myfunc的模块mymodule.py(通常只选择以下一种方式):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> import mypackage
>>> from mypackage import mymodule
>>> mymodule.myfunc()
Hello!
>>> import mypackage.mymodule
>>> mypackage.mymodule.myfunc()
Hello!
>>> from mypackage import mymodule
>>> mymodule.myfunc()
Hello!
>>> from mypackage.mymodule import myfunc
>>> myfunc()
Hello!
>>> from mypackage.mymodule import *
>>> myfunc()
Hello!

假设使用python 3,在:modules中有很好的文档

在包和模块的命名约定方面,PEP-0008中给出了通用指南-请参阅包和模块名称

模块应该有所有小写的短名称。如果下划线提高了可读性,则可以在模块名称中使用下划线。尽管不鼓励使用下划线,但python包也应该有短的、全小写的名称。


因为还没有人讨论过这个行动的问题:

What I wanted to do:

Make a python module install-able with"pip install ..."

这里是一个绝对最小的例子,展示了使用setuptoolstwine准备和上传包到pypi的基本步骤。

这绝不是至少阅读教程的替代品,它比这个非常基本的例子所涵盖的要多得多。

创建包本身已经包含在这里的其他答案中,所以让我们假设我们已经包含了这个步骤,并且我们的项目结构如下:

1
2
3
4
.
└── hellostackoverflow/
    ├── __init__.py
    └── hellostackoverflow.py

为了使用setuptools进行打包,我们需要添加一个文件setup.py,这将进入我们项目的根文件夹:

1
2
3
4
5
.
├── setup.py
└── hellostackoverflow/
    ├── __init__.py
    └── hellostackoverflow.py

我们至少为包指定元数据,我们的setup.py如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
from setuptools import setup

setup(
    name='hellostackoverflow',
    version='0.0.1',
    description='a pip-installable package example',
    license='MIT',
    packages=['hellostackoverflow'],
    author='Benjamin Gerfelder',
    author_email='[email protected]',
    keywords=['example'],
    url='https://github.com/bgse/hellostackoverflow'
)

由于我们已经设置了license='MIT',我们在项目中包含了一个名为LICENCE.txt的副本,以及restructuredtext中的readme文件,名为README.rst

1
2
3
4
5
6
7
.
├── LICENCE.txt
├── README.rst
├── setup.py
└── hellostackoverflow/
    ├── __init__.py
    └── hellostackoverflow.py

此时,我们准备开始使用setuptools进行包装,如果我们没有安装它,我们可以使用pip进行安装:

1
pip install setuptools

为了做到这一点并创建一个source distribution,在我们的项目根文件夹中,我们从命令行调用setup.py,指定我们需要sdist

1
python setup.py sdist

这将创建我们的分发包和蛋信息,并产生这样的文件夹结构,我们的包位于dist中:

1
2
3
4
5
6
7
8
9
.
├── dist/
├── hellostackoverflow.egg-info/
├── LICENCE.txt
├── README.rst
├── setup.py
└── hellostackoverflow/
    ├── __init__.py
    └── hellostackoverflow.py

此时,我们有一个可以使用pip安装的包,因此从我们的项目根目录(假设您具有与本例中类似的所有命名)开始:

1
pip install ./dist/hellostackoverflow-0.0.1.tar.gz

如果一切顺利的话,我们现在可以打开一个python解释器了,我会在项目目录之外的某个地方说,以避免混淆,并尝试使用我们闪亮的新包:

1
2
3
4
5
6
Python 3.5.2 (default, Sep 14 2017, 22:51:06)
[GCC 5.4.0 20160609] on linux
Type"help","copyright","credits" or"license" for more information.
>>> from hellostackoverflow import hellostackoverflow
>>> hellostackoverflow.greeting()
'Hello Stack Overflow!'

现在,我们已经确认了包的安装和工作,我们可以将其上传到pypi。

由于我们不想用我们的实验来污染实时存储库,因此我们为测试存储库创建一个帐户,并为上载过程安装twine

1
pip install twine

现在我们就快到了,创建了我们的帐户后,我们只需告诉twine上传我们的包,它会要求我们提供凭证,并将我们的包上传到指定的存储库:

1
twine upload --repository-url https://test.pypi.org/legacy/ dist/*

我们现在可以登录PYPI测试存储库的帐户,对我们刚上传的包感到惊奇一段时间,然后使用pip获取它:

1
pip install --index-url https://test.pypi.org/simple/ hellostackoverflow

如我们所见,基本过程并不十分复杂。正如我前面所说,它比这里介绍的内容要多得多,所以继续阅读教程以获得更深入的解释。


一旦定义了所选的命令,就可以简单地将保存的文件拖放到Python程序文件中的lib文件夹中。

1
2
>>> import mymodule
>>> mymodule.myfunc()


制作一个名为"hello.py"的文件

如果您使用的是python 2.x

1
2
def func():
    print"Hello"

如果您使用的是python 3.x

1
2
def func():
    print("Hello")

运行文件。然后,您可以尝试以下操作:

1
2
3
>>> import hello
>>> hello.func()
Hello

如果你想要有点困难,可以使用以下方法:

如果您使用的是python 2.x

1
2
def say(text):
    print text

如果您使用的是python 3.x

1
2
def say(text):
    print(text)

看到define旁边括号里的那个吗?这很重要。它可以在定义中使用。

文本-当你想让程序说出你想要的内容时,你可以使用它。根据它的名字,它是文本。我希望你知道课文的意思。它的意思是"单词"或"句子"。

运行文件。然后,如果您使用的是python 3.x,则可以尝试以下操作:

1
2
3
4
5
6
>>> import hello
>>> hello.say("hi")
hi
>>> from hello import say
>>> say("test")
test

对于python 2.x-我猜和python 3一样?不知道。如果我在python 2.x上犯了错误,请纠正我(我知道python 2,但我习惯于python 3)


我创建了一个项目来轻松地从头开始一个项目框架。https://github.com/machu-gwu/pygitrepo-project.

你可以创建一个测试项目,比如说,learn_creating_py_package

您可以了解为不同的目的应该拥有什么组件,例如:

  • 创建virtualenv
  • 安装本身
  • 运行统一测试
  • 运行代码覆盖率
  • 构建文档
  • 部署文档
  • 在不同的python版本中运行unittest
  • 部署到PyPI

使用pygitrepo的好处是,那些无聊的东西是自动创建的,并适应你的package_nameproject_namegithub_accountdocument host servicewindows or macos or linux

这是一个学习像专业人员一样开发Python项目的好地方。

希望这能有所帮助。

谢谢您。