6.PyCharm安装配置、模块、包、命名空间和作用域

2018-05-06  本文已影响0人  小哥_xiaoge

一、PyCharm下载及安装

学习Demo

官网下载地址选择平台及版本,社区版是免费的,专业版收费。若是选择专业版可先从官网下载安装包(官网才是最安全的),下载完成安装包后,双击打开,将左侧拖拽至右侧应用程序,默认安装。打开软件,在License server address中填入【https://jetlicense.nss.im/】,然后点击Activate即可顺利破解!配置什么的就不说了,网上一大把,自己动手丰衣足食。

二、模块

如果你退出 Python 解释器并重新进入,你做的任何定义(变量和方法)都会丢失。因此,如果你想要编写一些更大的程序,为准备解释器输入使用一个文本编辑器会更好,并以那个文件替代作为输入执行。这就是传说中的 脚本。随着你的程序变得越来越长,你可能想要将它分割成几个更易于维护的文件。你也可能想在不同的程序中使用顺手的函数,而不是把代码在它们之间中拷来拷去。

为了满足这些需要,Python 提供了一个方法可以从文件中获取定义,在脚本或者解释器的一个交互式实例中使用。这样的文件被称为 模块;模块中的定义可以 导入 到另一个模块或 主模块 中(在脚本执行时可以调用的变量集位于最高级,并且处于计算器模式)。

模块是包括 Python 定义和声明的文件。文件名就是模块名加上 .py 后缀。模块的模块名(作为一个字符串)可以由全局变量 __name__得到。例如,你可以用自己惯用的文件编辑器在当前目录下创建一个叫 ly_sum.py 的文件,录入如下内容:

#!/usr/bin/python3
# -*- coding: UTF-8 -*-

""" 表示模块的文档注释,任何模块代码的第一个字符串都被视为模块的文档注释 """
' a test module '  

""" 使用__author__变量把作者写进去,这样当你公开源代码后别人就可以瞻仰你的大名 """
__author__ = 'yang'  

def sum123(a, b):
    return a + b

def sum1234(a, b, c):
    return a + b + c
2.1 模块import语法:
2.1.1 import modulename

用import语句导入模块,就在当前的名称空间(namespace)建立了一个到该模块的引用.这种引用必须使用全称,也就是说,当使用在被导 入模块中定义的函数时,必须包含模块的名字。所以不能只使用 funcname,而应该使用 modname.funcname

2.1.2 from modulename import funcname

或者
from modname import fa, fb, fc

与第1种方法的区别:funcname 被直接导入到本地名字空间去了,所以它可以直接使用,而不需要加上模块名的限定

modname没有被定义,所以modname.funcname这种方式不起作用。并且,如果funcname如果已经被定义,它会被新版本(该导入模块中的版本)所替代。如果funcname被改成指向其他对象,modname不能不会觉察到。

2.1.3 from modulename import *

* 表示,该模块的所有公共对象(public objects)都被导入到 当前的名称空间,也就是任何只要不是以_开始的东西都会被导入。

1)如果你要经常访问模块的属性和方法,且不想一遍又一遍地敲入模块名,使用 from module import
2)如果你想要有选择地导入某些属性和方法,而不想要其它的,使用 from module import
3)如果模块包含的属性和方法与你的某个模块同名,你必须使用import module来避免名字冲突
4)尽量少用 from module import * ,因为判定一个特殊的函数或属性是从哪来的有些困难,并且会造成调试和重构都更困难

python有三种导入模块的方法:
import module1, module2,... moduleN
import本质:路径搜索和搜索路径。
理解:
在导入模块的时候,模块所在文件夹会自动生成一个pycache\module_name.cpython-36.pyc文件。
"import module_name" 的本质是将"module_name.py"中的全部代码加载到内存并赋值给与模块同名的变量写在当前文件中,这个变量的类型是'module';<module 'sys' (built-in)>
<module 'LYFibonacci.ly_fibo' from '/Users/yang/Desktop/PythonLearning/LYFibonacci/ly_fibo.py'>

"import package_name"导入包的本质就是执行该包下的init.py文件,在执行文件后,会在"package_name"目录下生成一个"pycache / init.cpython-36.pyc" 文件。

import ly_sum
print(ly_sum.sum123(90, 60))  # 150
from ly_sum import sum123,sum1234
print(sum.sum123(90, 60))  # 150
print(sum1234(10, 10, 10))  # 30

或者

""" 需要注意的是在实践中往往不鼓励从一个模块或包中使用 * 导入所有,因为这样会让代码变
得很难读"""
from ly_sum import * 
print(sum.sum123(90, 60))  # 150
print(sum1234(10, 10, 10))  # 30

下面三个都是正确的写法,调用的是os包里path子项的join函数,作用是将路径相加成一个完整路径:

from os.path import join
print(join('C:\\windows', 'system64'))
import os
print(os.path.join('C:\\windows', 'system64'))
import os.path
print(os.path.join('C:\\windows', 'system64'))

输出全是:
C:\windows\system64

2.2 “编译的” Python 文件

为了加快加载模块的速度,Python 会在__pycache__ 目录下以 module.version.pyc 名字缓存每个模块编译后的版本,这里的版本编制了编译后文件的格式。它通常会包含 Python 的版本号。例如,在 CPython 3.3 版中,spam.py 编译后的版本将缓存为 __pycache__/spam.cpython-33.pyc。这种命名约定允许由不同发布和不同版本的 Python 编译的模块同时存在。

Python 会检查源文件与编译版的修改日期以确定它是否过期并需要重新编译。这是完全自动化的过程。同时,编译后的模块是跨平台的,所以同一个库可以在不同架构的系统之间共享。

Python 不检查在两个不同环境中的缓存。首先,它会永远重新编译而且不会存储直接从命令行加载的模块。其次,如果没有源模块它不会检查缓存。若要支持没有源文件(只有编译版)的发布,编译后的模块必须在源目录下,并且必须没有源文件的模块。

部分高级技巧:

2.3 模块分类

Python模块分为三大类:

现在不做过多的介绍,后面的章节再介绍。

三、包

包通常是使用用“圆点模块名”的结构化模块命名空间。例如,名为A.B的模块表示了名为 A 的包中名为 B 的子模块。正如同用模块来保存不同的模块架构可以避免全局变量之间的相互冲突,使用圆点模块名保存像 NumPy 或 Python Imaging Library 之类的不同类库架构可以避免模块之间的命名冲突。

假设你现在想要设计一个模块集(一个“包”)来统一处理声音文件和声音数据。存在几种不同的声音格式(通常由它们的扩展名来标识,例如:.wav.aiff.au ),于是,为了在不同类型的文件格式之间转换,你需要维护一个不断增长的包集合。可能你还想要对声音数据做很多不同的操作(例如混音,添加回声,应用平衡 功能,创建一个人造效果),所以你要加入一个无限流模块来执行这些操作。你的包可能会是这个样子(通过分级的文件体系来进行分组):

sound/                          Top-level package
      __init__.py               Initialize the sound package
      formats/                  Subpackage for file format conversions
              __init__.py
              wavread.py
              wavwrite.py
              aiffread.py
              aiffwrite.py
              auread.py
              auwrite.py
              ...
      effects/                  Subpackage for sound effects
              __init__.py
              echo.py
              surround.py
              reverse.py
              ...
      filters/                  Subpackage for filters
              __init__.py
              equalizer.py
              vocoder.py
              karaoke.py
              ...

当导入这个包时,Python 通过 sys.path 搜索路径查找包含这个包的子目录。

为了让 Python 将目录当做内容包,目录中必须包含 __init__.py 文件。这是为了避免一个含有烂俗名字的目录无意中隐藏了稍后在模块搜索路径中出现的有效模块,比如 string。最简单的情况下,只需要一个空的 __init__.py 文件即可。当然它也可以执行包的初始化代码,或者定义稍后介绍的 __all__变量。

用户可以每次只导入包里的特定模块,例如:

import sound.effects.echo

这样就导入了 sound.effects.echo 子模块。它必需通过完整的名称来引用:

sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)

导入包时有一个可以选择的方式:

from sound.effects import echo

这样就加载了 echo 子模块,并且使得它在没有包前缀的情况下也可以使用,所以它可以如下方式调用:

echo.echofilter(input, output, delay=0.7, atten=4)

还有另一种变体用于直接导入函数或变量:

from sound.effects.echo import echofilter

这样就又一次加载了 echo 子模块,但这样就可以直接调用它的 echofilter() 函数:

echofilter(input, output, delay=0.7, atten=4)

需要注意的是使用 from package import item 方式导入包时,这个子项(item)既可以是包中的一个子模块(或一个子包),也可以是包中定义的其它命名,像函数、类或变量。import 语句首先核对是否包中有这个子项,如果没有,它假定这是一个模块,并尝试加载它。如果没有找到它,会引发一个 ImportError 异常。

相反,使用类似 import item.subitem.subsubitem 这样的语法时,这些子项必须是包,最后的子项可以是包或模块,但不能是前面子项中定义的类、函数或变量。

3.1 从 * 导入包

那么当用户写下 from sound.effects import * 时会发生什么事?理想中,总是希望在文件系统中找出包中所有的子模块,然后导入它们。这可能会花掉很长时间,并且出现期待之外的边界效应,导出了希望只能显式导入的包。

对于包的作者来说唯一的解决方案就是给提供一个明确的包索引。import 语句按如下条件进行转换:执行 from package import * 时,如果包中的 __init__.py 代码定义了一个名为 __all__ 的列表,就会按照列表中给出的模块名进行导入。新版本的包发布时作者可以任意更新这个列表。如果包作者不想 import * 的时候导入他们的包中所有模块,那么也可能会决定不支持它( import * )。例如, sound/effects/__init__.py 这个文件可能包括如下代码:

__all__ = ["echo", "surround", "reverse"]

这意味着 from sound.effects import * 语句会从 sound 包中导入以上三个已命名的子模块。

如果没有定义 __all__from sound.effects import * 语句不会从 sound.effects 包中导入所有的子模块。无论包中定义多少命名,只能确定的是导入了 sound.effects 包(可能会运行 __init__.py 中的初始化代码)以及包中定义的所有命名会随之导入。这样就从 __init__.py 中导入了每一个命名(以及明确导入的子模块)。同样也包括了前述的 import 语句从包中明确导入的子模块,考虑以下代码:

import sound.effects.echo
import sound.effects.surround
from sound.effects import *

在这个例子中,echosurround 模块导入了当前的命名空间,这是因为执行 from...import 语句时它们已经定义在 sound.effects 包中了(定义了 __all__ 时也会同样工作)。

尽管某些模块设计为使用 import * 时它只导出符合某种规范/模式的命名,仍然不建议在生产代码中使用这种写法。

记住,from Package import specific_submodule 没有错误!事实上,除非导入的模块需要使用其它包中的同名子模块,否则这是推荐的写法。

3.2 包内引用

如果包中使用了子包结构(就像示例中的 sound 包),可以按绝对位置从相邻的包中引入子模块。例如,如果 sound.filters.vocoder 包需要使用 sound.effects 包中的 echo 模块,它可以 from sound.Effects import echo

你可以用这样的形式from module import name 来写显式的相对位置导入。那些显式相对导入用点号标明关联导入当前和上级包。以 surround 模块为例,你可以这样用:

from . import echo
from .. import formats
from ..filters import equalizer

需要注意的是显式或隐式相对位置导入都基于当前模块的命名。因为主模块的名字总是 __main__,Python 应用程序的主模块应该总是用绝对导入。

3.3 多重目录中的包

包支持一个更为特殊的特性, path。 在包的 __init__.py 文件代码执行之前,该变量初始化一个目录名列表。该变量可以修改,它作用于包中的子包和模块的搜索功能。

这个功能可以用于扩展包中的模块集,不过它不常用。

四、命名空间和作用域

4.1 命名空间

命名空间 是从命名到对象的映射。当前命名空间主要是通过 Python 字典实现的,不过通常不关心具体的实现方式(除非出于性能考虑),以后也有可能会改变其实现方式。以下有一些命名空间的例子:内置命名(像 abs() 这样的函数,以及内置异常名)集,模块中的全局命名,函数调用中的局部命名。某种意义上讲对象的属性集也是一个命名空间。关于命名空间需要了解的一件很重要的事就是不同命名空间中的命名没有任何联系,例如两个不同的模块可能都会定义一个名为 maximize 的函数而不会发生混淆-用户必须以模块名为前缀来引用它们。

顺便提一句,我称 Python 中任何一个“.”之后的命名为 属性 --例如,表达式 z.real 中的 real 是对象 z 的一个属性。严格来讲,从模块中引用命名是引用属性:表达式 modname.funcname中,modname 是一个模块对象,funcname 是它的一个属性。因此,模块的属性和模块中的全局命名有直接的映射关系:它们共享同一命名空间!(有一个例外。模块对象有一个隐秘的只读对象,名为 __dict__ ,它返回用于实现模块命名空间的字典,命名__dict__ 是一个属性而非全局命名。显然,使用它违反了命名空间实现的抽象原则,应该被严格限制于调试中。)

属性可以是只读的或写的。后一种情况下,可以对属性赋值。你可以这样作: modname.the_answer = 42 。可写的属性也可以用 del 语句删除。例如: del modname.the_answer 会从 modname 对象中删除 the_answer 属性。

不同的命名空间在不同的时刻创建,有不同的生存期。包含内置命名的命名空间在 Python 解释器启动时创建,会一直保留,不被删除。模块的全局命名空间在模块定义被读入时创建,通常,模块命名空间也会一直保存到解释器退出。由解释器在最高层调用执行的语句,不管它是从脚本文件中读入还是来自交互式输入,都是 main 模块的一部分,所以它们也拥有自己的命名空间(内置命名也同样被包含在一个模块中,它被称作 builtins )。

当调用函数时,就会为它创建一个局部命名空间,并且在函数返回或抛出一个并没有在函数内部处理的异常时被删除。(实际上,用遗忘来形容到底发生了什么更为贴切。)当然,每个递归调用都有自己的局部命名空间。

4.2 作用域

作用域 就是一个 Python 程序可以直接访问命名空间的正文区域。这里的直接访问意思是一个对名称的错误引用会尝试在命名空间内查找。尽管作用域是静态定义,在使用时他们都是动态的。每次执行时,至少有三个命名空间可以直接访问的作用域嵌套在一起:

如果一个命名声明为全局的,那么对它的所有引用和赋值会直接搜索包含这个模块全局命名的作用域。如果要重新绑定最里层作用域之外的变量,可以使用 nonlocal 语句;如果不声明为 nonlocal,这些变量将是只读的(对这样的变量赋值会在最里面的作用域创建一个新的局部变量,外部具有相同命名的那个变量不会改变)。

通常,局部作用域引用当前函数的命名。在函数之外,局部作用域与全局使用域引用同一命名空间:模块命名空间。类定义也是局部作用域中的另一个命名空间。

重要的是作用域决定于源程序的意义:一个定义于某模块中的函数的全局作用域是该模块的命名空间,而不是该函数的别名被定义或调用的位置,了解这一点非常重要。另一方面,命名的实际搜索过程是动态的,在运行时确定的。然而,Python 语言也在不断发展,以后有可能会成为静态的“编译”时确定,所以不要依赖动态解析!(事实上,局部变量已经是静态确定了。)

Python 的一个特别之处在于:如果没有使用 global 语法,其赋值操作总是在最里层的作用域。赋值不会复制数据,只是将命名绑定到对象。删除也是如此:del x 只是从局部作用域的命名空间中删除命名 x 。事实上,所有引入新命名的操作都作用于局部作用域。特别是 import 语句和函数定义将模块名或函数绑定于局部作用域(可以使用 global 语句将变量引入到全局作用域)。

global 语句用以指明某个特定的变量为全局作用域,并重新绑定它。nonlocal 语句用以指明某个特定的变量为封闭作用域,并重新绑定它。

在一个模块中,我们可能会定义很多函数和变量,但有的函数和变量我们希望给别人使用,有的函数和变量我们希望仅仅在模块内部使用。在Python中,是通过_前缀来实现的。

正常的函数和变量名是公开的(public),可以被直接引用,比如:abcx123PI等;

类似__xxx__这样的变量是特殊变量,可以被直接引用,但是有特殊用途,比如上面的__author____name__就是特殊变量,hello模块定义的文档注释也可以用特殊变量__doc__访问,我们自己的变量一般不要用这种变量名;

类似_xxx__xxx这样的函数或变量就是非公开的(private),不应该被直接引用,比如_abc__abc等;

之所以我们说,private函数和变量“不应该”被直接引用,而不是“不能”被直接引用,是因为Python并没有一种方法可以完全限制访问private函数或变量,但是,从编程习惯上不应该引用private函数或变量。

private函数或变量不应该被别人引用,那它们有什么用呢?请看例子:

def _private_1(name):
    return 'Hello, %s' % name

def _private_2(name):
    return 'Hi, %s' % name

def greeting(name):
    if len(name) > 3:
        return _private_1(name)
    else:
        return _private_2(name)

我们在模块里公开greeting()函数,而把内部逻辑用private函数隐藏起来了,这样,调用greeting()函数不用关心内部的private函数细节,这也是一种非常有用的代码封装和抽象的方法,即:

外部不需要引用的函数全部定义成private,只有外部需要引用的函数才定义为public。

4.3 作用域和命名空间示例

以下是一个示例,演示了如何引用不同作用域和命名空间,以及 globalnonlocal 如何影响变量绑定:

def scope_test():
    def do_local():
        spam = "local spam"
    def do_nonlocal():
        nonlocal spam
        spam = "nonlocal spam"
    def do_global():
        global spam
        spam = "global spam"
    spam = "test spam"
    do_local()
    print("After local assignment:", spam)
    do_nonlocal()
    print("After nonlocal assignment:", spam)
    do_global()
    print("After global assignment:", spam)

scope_test()
print("In global scope:", spam)

以上示例代码的输出为:

After local assignment: test spam
After nonlocal assignment: nonlocal spam
After global assignment: nonlocal spam
In global scope: global spam

注意:local 赋值语句是无法改变 scope_testspam 绑定。nonlocal 赋值语句改变了 scope_testspam 绑定,并且 global 赋值语句从模块级改变了 spam 绑定。

你也可以看到在 global 赋值语句之前对 spam 是没有预先绑定的。

上一篇下一篇

猜你喜欢

热点阅读