第3章 模块化

        相关函数的集合组合成一个整体的形式,该形式称为模块(module)。

        模块就是可以被其他代码引用的源代码文件,可以在一个模块中使用定义在另一个模块中的函数。

3.1 在一个.py文件中组织代码

        任意的文本编辑器——最好支持语法高亮,按Tab键自动缩进4个空格。用UTF-8编码保存文件,扩展名为.py。

# words.py

from urllib.request import urlopen

with urlopen('http://sixty-north.com/c/t.txt') as story:
    storyWords = []
    for line in story:
        lineWords = line.decode('utf-8').split()
        for word in lineWords:
            storyWords.append(word)

for word in storyWords:
    print(word)

3.1.1 从操作系统shell运行Python程序

        在.py文件所在目录运行:

        python <文件名.py>

3.1.2 将模块导入到REPL中

>>> import words 
It
was
the
best
of
times
it
was
the
worst
of
times
...

3.2 定义函数

        定义函数使用def关键字,后跟函数名,然后是一个由括号包裹的参数列表以及一个冒号来定义新的代码块。

>>> def square(x):
...     return x * x
...
>>> square(5)
25

        函数遇return语句返回,如果没有返回值,Python将隐式返回None。

>>> def evenOrOdd(n):
...     if n % 2 == 0:
...             print('even')
...             return
...     print('odd')
...
>>> evenOrOdd(4)
even
>>> evenOrOdd(5)
odd
>>>
>>> w = evenOrOdd(3)
odd
>>> w is None
True

3.3 将模块组织成函数

        使用函数来组织words模块

# words.py

from urllib.request import urlopen

def fetchWords():   
    with urlopen('http://sixty-north.com/c/t.txt') as story:
        storyWords = []
        for line in story:
            lineWords = line.decode('utf-8').split()
            for word in lineWords:
                storyWords.append(word)

    for word in storyWords:
        print(word)
>>> import words
>>> words.fetchWords()
It
was
the
best
of
times
...
>>> from words import fetchWords
>>> fetchWords() 
It
was
the
best
of
times
...

3.3.1  __name__类型以及从命令行里执行模块

        __name__的发音是“dunder name”。导入模块时,__name__的值是模块的名称;将模块作为脚本运行时__name__的值为__main__。

        在words.py模块末尾加入以下代码

        print(__name__)

>>> import words
words

>>> import words  # 导入模块只执行一次
>>>

\pyfund> python words.py
__main__

        使用下面的if语句替换print语句

        if __name__ == '__main__':

            fetchWords()

>>> import words
>>> 

\pyfund> python words.py
It
was
the
best
of
times
...

3.4 Python的执行模型

        执行模型(execution model),指的是模块导入和执行期间的函数定义以及其他重要事件的准确的规则定义。

        与C++、Java和C#等编译语言不同,Python没有编译器,除了源码之外,函数不会以任何形式存在——直到执行。实际上,由于只有在导入函数并处理def语句时,函数才会被确定,因此未被导入的模块中的函数永远不会被确定。

        任何.py文件都可以构成一个Python模块,但是编写模块是为了方便导入或执行,即使是简单的脚本也做成可导入的,使用if __name__ == "__main__"惯用句法可以达到同样的效果。

3.5 创建带有命令行参数的主函数

        重构words.py模块,将单词检索和收集与单词的输出分开

# words.py

import sys
from urllib.request import urlopen


# 获取单词并返回一个单词列表
def fetchWords(url):

    with urlopen(url) as story:
        storyWords = []
        for line in story:
            lineWords = line.decode('utf-8').split()
            for word in lineWords:
                storyWords.append(word)

    return storyWords


# 输出单词列表
def printItems(items):
    for word in items:
        print(word)


def main(url):
    words = fetchWords(url)
    printItems(words)


if __name__ == '__main__':
    main(sys.argv[1])

3.6 禅之刻

        PEP 8风格指南通常在模块级函数之间使用两条空行。 

3.7 docstrings

        自动文档功能,Python中的API文档使用了名为docstrings的工具。docstrings是字符串字面量,一般为命名代码块中的第一个语句,如函数或模块,使用三引号字符串。 

        给fetchWords()函数增加文档,倾向使用Google的Python风格指南中提供的形式,因为它可以在计算机上解析,同时在控制台上仍然可读。        

def fetchWords(url):
    """从URL获取单词列表。
    
    args:
        url:UTF-8文本文档的URL。
        
    returns:
        包含文档中单词的字符串列表。
    """
    with urlopen(url) as story:

        storyWords = []
        for line in story:
            lineWords = line.decode('utf-8').split()
            for word in lineWords:
                storyWords.append(word)

    return storyWords
>>> from words import * 
>>> help(fetchWords)
Help on function fetchWords in module words:

fetchWords(url)
    从URL获取单词列表。

    args:
        url:UTF-8文本文档的URL。   

    returns:
        包含文档中单词的字符串列表。

        为模块和其他函数添加上帮助文档:

"""从URL中检索并输出单词

Usage:
    python3 words.py <URL>
"""
import sys
from urllib.request import urlopen


# 获取单词并返回一个单词列表
def fetchWords(url):
    """从URL获取单词列表。
    
    args:
        url:UTF-8文本文档的URL。
        
    returns:
        包含文档中单词的字符串列表。
    """
    with urlopen(url) as story:

        storyWords = []
        for line in story:
            lineWords = line.decode('utf-8').split()
            for word in lineWords:
                storyWords.append(word)

    return storyWords


# 输出单词列表
def printItems(items):
    """每行输出一项
    Args:
        items:可迭代,可输出的一系列项目。

    Returns:
        包含文档中单词的字符串列表。
    """
    for word in items:
        print(word)


def main(url):
    """输出从URL获得的文本文档中的每一个单词。
    Args:
        url:UTF-8文本文档的URL
    """
    words = fetchWords(url)
    printItems(words)


if __name__ == '__main__':
    main(sys.argv[1])

        在整个模块上请求help()时,会得到很多有用的信息:

>>> import words
>>> help(words)
Help on module words:

NAME
    words - 从URL中检索并输出单词

DESCRIPTION
    Usage:
        python3 words.py <URL>

FUNCTIONS
    fetchWords(url)
        从URL获取单词列表。

        args:
            url:UTF-8文本文档的URL。

        returns:
            包含文档中单词的字符串列表。      

    main(url)
        输出从URL获得的文本文档中的每一个单词 
        Args:
            url:UTF-8文本文档的URL

    printItems(items)
        每行输出一项
        Args:
            items:可迭代,可输出的一系列项目

        Returns:
            包含文档中单词的字符串列表。      

FILE
    d:\python\写给程序员的python教程\pyfund\words.py

3.8 注释

        Python代码中的大多数文档都应该放在docstrings中。docstrings解释了如何使用模块提供的功能,而不是讲解它的工作原理。理想情况下,代码应该足够干净,不需要辅助说明。

        然而,有时候有必要解释为什么要选择特定的方法或使用特定的技术,可以用Python的注释来完成。

        Python中的注释以#开始,在行尾结束

if __name__ == '__main__':
    main(sys.argv[1])  # 第一个参数是模块的文件名称。

3.10 小结

  • Python模块
  • 放在*.py文件中的Python代码叫模块。
  • 通过将模块作为Python解释器的第一个参数传入可直接运行它们。
  • 可以将模块导入REPL,此时,模块中所有顶级声明都会按顺序执行。
  • Python函数
  • 命名函数使用def关键字定义,后跟函数名称和由括号包裹的参数列表。
  • 函数可以使用return语句返回对象。
  • 无参的return语句返回None,每个函数体末尾也隐式返回None。
  • 模块执行
  • 我们可以通过检查__name__变量的值来检测模块是否已被导入或执行。如果它等于字符串__main__,模块直接作为程序执行。在模块结尾使用顶级语句if __name__ == '__main__'惯用句法,如果满足这个条件,我们可以执行函数,从而可以有效地导入和执行模块,还可以给短脚本增加重要的测试。
  • 模块中的代码只会在第一次导入的时候被执行一次。
  • def是将可执行代码绑定到函数名的语句。
  • 可以通过sys模块的argv属性访问字符串列表形式的命令行参数。索引为0的命令行参数是脚本文件名,因此索引为1 的项才是第一个真正的参数。
  • Python动态类型意味着我们的函数对于参数的类型可能是通用的。
  • Docstrings
  • 函数的docstrings是位于函数定义第一行的字符串字面量。它们通常是由三引号包裹的,包含使用信息的多行字符串。
  • 可以使用REPL中的help()来检索docstrings提供的函数文档。
  • 模块docstrings放置在模块的开头,放在任何Python语句(如import语句)之前。
  • 注释
  • Python中的注释以#号开头,并持续到行尾。