如何编写Python模块/软件包?

问题:如何编写Python模块/软件包?

我一直在为工作中的简单任务制作Python脚本,从来没有真正打扰过将它们打包供其他人使用。现在,我被分配为REST API制作Python包装器。我对如何开始一无所知,我需要帮助。

我有的:

(只想尽可能地具体一点)我已经准备好virtualenv,它也位于github上,还存在用于python的.gitignore文件,以及用于与REST API交互的请求库。而已。

这是当前目录树

.
├── 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文件放在哪里。

我想做的是:

使用“ pip install …”制作可安装的python模块

如果可能的话,我需要一个逐步的编写Python模块的逐步过程。

I’ve been making Python scripts for simple tasks at work and never really bothered packaging them for others to use. Now I have been assigned to make a Python wrapper for a REST API. I have absolutely no idea on how to start and I need help.

What I have:

(Just want to be specific as possible) I have the virtualenv ready, it’s also up in github, the .gitignore file for python is there as well, plus, the requests library for interacting with the REST API. That’s it.

Here’s the current directory tree

.
├── bin
│   └── /the usual stuff/
├── include
│   └── /the usual stuff/
├── lib
│   └── python2.7
│       └── /the usual stuff/
├── local
│   └── /the usual stuff/
└── README.md

27 directories, 280 files

I don’t even know where to put the .py files, if I ever make one.

What I wanted to do:

Make a python module install-able with “pip install …”

If possible, I want a general step by step process on writing Python modules.


回答 0

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

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

def helloworld():
   print "hello"

然后,您可以导入hello

>>> import hello
>>> hello.helloworld()
'hello'
>>>

要对许多.py文件进行分组,请将它们放在文件夹中。带有的任何文件夹__init__.py都被python视为模块,您可以将其称为包

|-HelloModule
  |_ __init__.py
  |_ hellomodule.py

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

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

A module is a file containing Python definitions and statements. The file name is the module name with the suffix .py

create hello.py then write the following function as its content:

def helloworld():
   print "hello"

Then you can import hello:

>>> import hello
>>> hello.helloworld()
'hello'
>>>

To group many .py files put them in a folder. Any folder with an __init__.py is considered a module by python and you can call them a package

|-HelloModule
  |_ __init__.py
  |_ hellomodule.py

You can go about with the import statement on your module the usual way.

For more information, see 6.4. Packages.


回答 1

Python 3-更新于2015年11月18日

认为已接受的答案很有用,但希望根据我自己的经验在几个方面进行扩展,以使他人受益。

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

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

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

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

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

>>> import mymodule
>>> mymodule.myfunc()
Hello!
>>> from mymodule import myfunc
>>> myfunc()
Hello!
>>> from mymodule import *
>>> myfunc()
Hello!

好的,这很容易。

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

:包是通过使用“点分模块名称”来构造Python模块命名空间的一种方式。例如,模块名称AB在名为A的包中指定了一个名为B的子模块。就像使用模块使不同模块的作者免于担心彼此的全局变量名一样,使用带点划线的模块名称也节省了作者诸如NumPy或Python Imaging Library之类的多模块软件包,而不必担心彼此的模块名称。

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

.
└── mypackage
    ├── __init__.py
    └── mymodule.py

__init__.py文件是使Python将目录视为包含包的必需文件。有关更多信息,请参见稍后提供的模块文档链接。

我们当前的工作目录位于名为mypackage的普通文件夹之上的一级

$ ls
mypackage

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

>>> 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,以下位置提供了出色的文档:模块

关于程序包和模块的命名约定,PEP-0008中给出了通用准则-请参阅程序包和模块名称

模块应使用简短的全小写名称。如果模块名称可以提高可读性,则可以在模块名称中使用下划线。尽管不鼓励使用下划线,但Python软件包也应使用短小写全名。

Python 3 – UPDATED 18th November 2015

Found the accepted answer useful, yet wished to expand on several points for the benefit of others based on my own experiences.

Module: A module is a file containing Python definitions and statements. The file name is the module name with the suffix .py appended.

Module Example: Assume we have a single python script in the current directory, here I am calling it mymodule.py

The file mymodule.py contains the following code:

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

If we run the python3 interpreter from the current directory, we can import and run the function myfunc in the following different ways (you would typically just choose one of the following):

>>> import mymodule
>>> mymodule.myfunc()
Hello!
>>> from mymodule import myfunc
>>> myfunc()
Hello!
>>> from mymodule import *
>>> myfunc()
Hello!

Ok, so that was easy enough.

Now assume you have the need to put this module into its own dedicated folder to provide a module namespace, instead of just running it ad-hoc from the current working directory. This is where it is worth explaining the concept of a package.

Package: Packages are a way of structuring Python’s module namespace by using “dotted module names”. For example, the module name A.B designates a submodule named B in a package named A. Just like the use of modules saves the authors of different modules from having to worry about each other’s global variable names, the use of dotted module names saves the authors of multi-module packages like NumPy or the Python Imaging Library from having to worry about each other’s module names.

Package Example: Let’s now assume we have the following folder and files. Here, mymodule.py is identical to before, and __init__.py is an empty file:

.
└── mypackage
    ├── __init__.py
    └── mymodule.py

The __init__.py files are required to make Python treat the directories as containing packages. For further information, please see the Modules documentation link provided later on.

Our current working directory is one level above the ordinary folder called mypackage

$ ls
mypackage

If we run the python3 interpreter now, we can import and run the module mymodule.py containing the required function myfunc in the following different ways (you would typically just choose one of the following):

>>> 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!

Assuming Python 3, there is excellent documentation at: Modules

In terms of naming conventions for packages and modules, the general guidelines are given in PEP-0008 – please see Package and Module Names

Modules should have short, all-lowercase names. Underscores can be used in the module name if it improves readability. Python packages should also have short, all-lowercase names, although the use of underscores is discouraged.


回答 2

由于尚无人讨论过OP的这个问题:

我想做的是:

使用“ pip install …”制作可安装的python模块

这是一个绝对的最小示例,显示了使用setuptools和准备软件包并将其上传到PyPI的基本步骤twine

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

在这里,其他答案已经涵盖了创建包本身,因此,让我们假设我们已经覆盖了这一步,并且我们的项目结构如下:

.
└── hellostackoverflow/
    ├── __init__.py
    └── hellostackoverflow.py

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

.
├── setup.py
└── hellostackoverflow/
    ├── __init__.py
    └── hellostackoverflow.py

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

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='benjamin.gerfelder@gmail.com',
    keywords=['example'],
    url='https://github.com/bgse/hellostackoverflow'
)

设置好之后license='MIT',我们在项目中添加了一个副本,LICENCE.txt在reStructuredText中添加了一个自述文件,如下所示README.rst

.
├── LICENCE.txt
├── README.rst
├── setup.py
└── hellostackoverflow/
    ├── __init__.py
    └── hellostackoverflow.py

此时,我们准备开始使用进行打包setuptools,如果尚未安装,可以使用以下命令进行安装pip

pip install setuptools

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

python setup.py sdist

这将创建我们的分发程序包和egg-info,并导致这样的文件夹结构,我们的程序包位于dist

.
├── dist/
├── hellostackoverflow.egg-info/
├── LICENCE.txt
├── README.rst
├── setup.py
└── hellostackoverflow/
    ├── __init__.py
    └── hellostackoverflow.py

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

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

如果一切顺利,我们现在可以打开一个Python解释器,我想在项目目录之外的某个地方说以避免任何混淆,然后尝试使用新的闪亮包:

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为上载过程进行安装:

pip install twine

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

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

现在,我们可以在PyPI测试库上登录我们的帐户,惊叹我们刚上传的软件包一段时间,然后使用pip以下代码进行抓取:

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

我们可以看到,基本过程不是很复杂。正如我之前所说的,它所包含的内容远不止本文所述,因此请继续阅读本教程以获取更深入的说明。

Since nobody did cover this question of the OP yet:

What I wanted to do:

Make a python module install-able with “pip install …”

Here is an absolute minimal example, showing the basic steps of preparing and uploading your package to PyPI using setuptools and twine.

This is by no means a substitute for reading at least the tutorial, there is much more to it than covered in this very basic example.

Creating the package itself is already covered by other answers here, so let us assume we have that step covered and our project structure like this:

.
└── hellostackoverflow/
    ├── __init__.py
    └── hellostackoverflow.py

In order to use setuptools for packaging, we need to add a file setup.py, this goes into the root folder of our project:

.
├── setup.py
└── hellostackoverflow/
    ├── __init__.py
    └── hellostackoverflow.py

At the minimum, we specify the metadata for our package, our setup.py would look like this:

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='benjamin.gerfelder@gmail.com',
    keywords=['example'],
    url='https://github.com/bgse/hellostackoverflow'
)

Since we have set license='MIT', we include a copy in our project as LICENCE.txt, alongside a readme file in reStructuredText as README.rst:

.
├── LICENCE.txt
├── README.rst
├── setup.py
└── hellostackoverflow/
    ├── __init__.py
    └── hellostackoverflow.py

At this point, we are ready to go to start packaging using setuptools, if we do not have it already installed, we can install it with pip:

pip install setuptools

In order to do that and create a source distribution, at our project root folder we call our setup.py from the command line, specifying we want sdist:

python setup.py sdist

This will create our distribution package and egg-info, and result in a folder structure like this, with our package in dist:

.
├── dist/
├── hellostackoverflow.egg-info/
├── LICENCE.txt
├── README.rst
├── setup.py
└── hellostackoverflow/
    ├── __init__.py
    └── hellostackoverflow.py

At this point, we have a package we can install using pip, so from our project root (assuming you have all the naming like in this example):

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

If all goes well, we can now open a Python interpreter, I would say somewhere outside our project directory to avoid any confusion, and try to use our shiny new package:

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!'

Now that we have confirmed the package installs and works, we can upload it to PyPI.

Since we do not want to pollute the live repository with our experiments, we create an account for the testing repository, and install twine for the upload process:

pip install twine

Now we’re almost there, with our account created we simply tell twine to upload our package, it will ask for our credentials and upload our package to the specified repository:

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

We can now log into our account on the PyPI test repository and marvel at our freshly uploaded package for a while, and then grab it using pip:

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

As we can see, the basic process is not very complicated. As I said earlier, there is a lot more to it than covered here, so go ahead and read the tutorial for more in-depth explanation.


回答 3

定义所选命令后,只需将保存的文件拖放到python程序文件中的Lib文件夹中即可。

>>> import mymodule 
>>> mymodule.myfunc()

Once you have defined your chosen commands, you can simply drag and drop the saved file into the Lib folder in your python program files.

>>> import mymodule 
>>> mymodule.myfunc()

回答 4

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

如果您使用的是Python 2.x

def func():
    print "Hello"

如果您使用的是Python 3.x

def func():
    print("Hello")

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

>>> import hello
>>> hello.func()
Hello

如果您想稍微努力一点,可以使用以下方法:

如果您使用的是Python 2.x

def say(text):
    print text

如果您使用的是Python 3.x

def say(text):
    print(text)

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

文本-​​当您希望程序说出想要的内容时可以使用它。根据其名称,它是文本。希望您知道文本的含义。它的意思是“单词”或“句子”。

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

>>> 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一起使用)

Make a file named “hello.py”

If you are using Python 2.x

def func():
    print "Hello"

If you are using Python 3.x

def func():
    print("Hello")

Run the file. Then, you can try the following:

>>> import hello
>>> hello.func()
Hello

If you want a little bit hard, you can use the following:

If you are using Python 2.x

def say(text):
    print text

If you are using Python 3.x

def say(text):
    print(text)

See the one on the parenthesis beside the define? That is important. It is the one that you can use within the define.

Text – You can use it when you want the program to say what you want. According to its name, it is text. I hope you know what text means. It means “words” or “sentences”.

Run the file. Then, you can try the following if you are using Python 3.x:

>>> import hello
>>> hello.say("hi")
hi
>>> from hello import say
>>> say("test")
test

For Python 2.x – I guess same thing with Python 3? No idea. Correct me if I made a mistake on Python 2.x (I know Python 2 but I am used with Python 3)


回答 5

我创建了一个项目,以轻松地从头开始创建项目框架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

这是学习像pro一样开发python项目的好地方。

希望这会有所帮助。

谢谢。

I created a project to easily initiate a project skeleton from scratch. https://github.com/MacHu-GWU/pygitrepo-project.

And you can create a test project, let’s say, learn_creating_py_package.

You can learn what component you should have for different purpose like:

  • create virtualenv
  • install itself
  • run unittest
  • run code coverage
  • build document
  • deploy document
  • run unittest in different python version
  • deploy to PYPI

The advantage of using pygitrepo is that those tedious are automatically created itself and adapt your package_name, project_name, github_account, document host service, windows or macos or linux.

It is a good place to learn develop a python project like a pro.

Hope this could help.

Thank you.