如何设置一个Python项目?

1 投票
5 回答
1881 浏览
提问于 2025-04-16 08:00

我正在做一些比较复杂的命令行操作(其实不是基于网页的),而且我刚开始接触Python,所以我想知道应该怎么安排我的文件和文件夹等等。有没有那种“头文件”,可以把所有数据库连接的内容放在里面?

我应该在哪里定义类和对象呢?

5 个回答

0

你可以根据自己应用的需求来组织代码,怎么方便怎么来。我不太清楚你具体在做什么,所以不能确定对你来说最好的组织方式是什么,但你可以随意拆分代码,只需要导入你需要的部分就行。

你可以在任何文件中定义类,而且在一个脚本里可以定义任意数量的类(这和Java不一样)。没有官方的头文件(不像C或C++那样),但你可以使用配置文件来存储连接数据库的信息等等,然后用configparser(这是一个标准库的功能)来整理这些信息。

把相似的东西放在同一个文件里是个好主意,比如如果你有一个图形用户界面(GUI),你可以为界面创建一个文件;如果你有一个命令行界面(CLI),可以把它单独放在一个文件里。文件的组织方式不是最重要的,关键是你的代码是如何组织成类和函数的。

3

每个Python源文件都是一个模块。Python没有“头文件”。基本的想法是,当你导入“foo”时,它会加载“foo.py”中的代码(或者是之前编译过的版本)。然后你可以通过说foo.whatever来访问foo模块里的内容。

在Python代码中,似乎有两种组织方式。一些项目使用扁平布局,也就是所有模块都在顶层。另一些则使用层级结构。你可以通过导入“foo.bar.baz”来导入foo/bar/baz.py。使用层级布局时一个重要的注意事项是,在适当的目录下要有__init__.py文件(这个文件可以是空的,但必须存在)。

类的定义方式如下:

class MyClass(object):
    def __init__(self, x):
        self.x = x

    def printX(self):
        print self.x

要创建一个实例:

z = MyObject(5)
8

这里给你一个典型的Python模块的例子,里面有一些解释。这是一个名为“Dims.py”的文件。这里不是整个文件,只是一些部分,让你了解里面的内容。

#!/usr/bin/env python

这行是标准的开头,告诉系统如何执行这个文件。用/usr/bin/env python代替/usr/bin/python,是让系统通过用户的PATH来找到Python;你想要的Python可能在~/bin或者/usr/local/bin里。

"""Library for dealing with lengths and locations."""

如果文件的第一行是一个字符串,那就是这个模块的文档字符串。文档字符串是紧跟在某个项目开始后面的字符串,可以通过它的__doc__属性访问。在这个例子中,因为它是模块的文档字符串,如果用户用import Dims导入这个文件,那么Dims.__doc__就会返回这个字符串。

# Units
MM_BASIC = 1500000
MILS_BASIC = 38100
IN_BASIC = MILS_BASIC * 1000

有很多好的格式和命名规范的指导,记录在一个叫做PEP 8的文档里。这些是模块级别的变量(实际上是常量),所以它们用全大写字母和下划线来书写。其实我并不是完全遵循所有规则;老习惯很难改。既然你是新手,尽量遵循PEP 8,除非你实在做不到。

_SCALING = 1
_SCALES = {
    mm_basic: MM_BASIC,
    "mm": MM_BASIC,
    mils_basic: MILS_BASIC,
    "mil": MILS_BASIC,
    "mils": MILS_BASIC,
    "basic": 1,
    1: 1
}

这些模块级别的变量名字前面有下划线。这给它们提供了一定的“隐私”,也就是说import Dims不会让你访问Dims._SCALING。不过,如果你需要使用它,可以明确地写import Dims._SCALING as scaling

def UnitsToScale(units=None):
    """Scales the given units to the current scaling."""
    if units is None:
        return _SCALING
    elif units not in _SCALES:
        raise ValueError("unrecognized units: '%s'." % units)
    return _SCALES[units]

UnitsToScale是一个模块级别的函数。注意文档字符串和默认值及异常的使用。在默认值声明中,=周围没有空格。

class Length(object):
    """A length.  Makes unit conversions easier.

    The basic, mm, and mils properties can be used to get or set the length 
    in the desired units.
    >>> x = Length(mils=1000)
    >>> x.mils
    1000.0
    >>> x.mm
    25.399999999999999
    >>> x.basic
    38100000L
    >>> x.mils = 100
    >>> x.mm
    2.54
    """

这是一个类的声明。注意文档字符串里有一些看起来像Python命令行命令的东西。这些叫做文档测试,因为它们是在文档字符串里的测试代码。稍后会详细讲。

    def __init__(self, unscaled=0, basic=None, mm=None, mils=None, units=None):
        """Constructs a Length.

        Default contructor creates a length of 0.
        >>> Length()
        Length(basic=0)

        Length(<float>) or Length(<string>) creates a length with the given 
        value at the current scale factor.
        >>> Length(1500)
        Length(basic=1500)
        >>> Length("1500")
        Length(basic=1500)
        """

        # Straight copy
        if isinstance(unscaled, Length):
            self._x = unscaled._x
            return

        # rest omitted

这是初始化器。和C++不同,你只有一个初始化器,但你可以使用默认参数让它看起来像有多个构造函数。

    def _GetBasic(self): return self._x
    def _SetBasic(self, x): self._x = x
    basic = property(_GetBasic, _SetBasic, doc="""
        This returns the length in basic units.""")

这是一个属性。它允许你在使用和访问其他数据成员相同的语法时,拥有获取和设置的功能,比如myLength.basic = 10myLength._SetBasic(10)是一样的。因为可以这样做,所以你不应该默认为数据成员写获取和设置函数。直接操作数据成员就可以了。如果以后需要获取和设置函数,可以把数据成员转换为属性,这样模块的用户就不需要改他们的代码。注意文档字符串是在属性上,而不是在获取和设置函数上。

如果你有一个只读的属性,可以用property作为装饰器来声明它。例如,如果上面的属性是只读的,我会这样写:

    @property
    def basic(self):
        """This returns the length in basic units."""
        return self._x

注意,属性的名字就是获取方法的名字。在Python 2.6或更高版本中,你也可以用装饰器来声明设置方法。

    def __mul__(self, other):
        """Multiplies a Length by a scalar.

        >>> Length(10)*10
        Length(basic=100)
        >>> 10*Length(10)
        Length(basic=100)
        """
        if type(other) not in _NumericTypes:
            return NotImplemented
        return Length(basic=self._x * other)

这段代码重载了*运算符。注意,你可以返回特殊值NotImplemented来告诉Python这个操作没有实现(在这种情况下,如果你尝试用非数字类型,比如字符串来相乘)。

    __rmul__ = __mul__

因为代码和其他值一样,所以你可以把一个方法的代码赋值给另一个。这行代码告诉Python,something * Length操作使用和Length * something相同的代码。不要重复自己。

现在类声明完了,我可以回到模块代码。在这个例子中,我有一些代码只想在这个文件被直接执行时运行,而不是作为模块被导入。所以我使用以下测试:

if __name__ == "__main__":

然后if里的代码只有在直接运行时才会执行。在这个文件里,我有以下代码:

    import doctest
    doctest.testmod()

这段代码会遍历模块中的所有文档字符串,查找看起来像Python提示符后面跟命令的行。后面的行被认为是命令的输出。如果命令输出了其他内容,测试就被认为失败,实际输出会被打印出来。想了解更多细节,可以阅读doctest模块的文档。

最后一点关于文档测试的说明:它们很有用,但不是最灵活或全面的测试方法。想要更全面的测试,你可以了解一下单元测试(unittest模块)。

撰写回答