模块

1.搜索路径

默认搜索路径是在编译或是安装时指定的。 它可以在一个或两个地方修改。

  1. 一个是启动 Python 的 shell 或命令行的 PYTHONPATH 环境变量。
  2. 解释器启动之后, 也可以访问这个搜索路径, 它会被保存在 sys 模块的 sys.path 变量里。不过它不是冒号分割的字符串, 而是包含每个独立路径的列表。你可以通过sys.path.append('路径')修改搜索路径。

假设我在桌面写了一段python脚本,现在我要在IDLE里import它,就可以先把桌面的path加入到sys.path中。

2.名称空间

(1)概念

名称空间是名称到对象的映射。

在执行期间有两个或三个活动的名称空间。 这三个名称空间分别是局部名称空间, 全局名称空间内建名称空间, 但局部名称空间在执行期间是不断变化的, 所以我们说”两个或三个”。

Python 解释器首先加载内建名称空间。 它由 __builtins__ 模块中的名字构成。 随后加载执行模块的全局名称空间, 它会在模块开始执行后变为活动名称空间。 这样我们就有了两个活动的名称空间。如果在执行期间调用了一个函数, 那么将创建出第三个名称空间, 即局部名称空间。

(2)名称空间和变量作用域比较

名称空间是纯粹意义上的名字和对象间的映射关系, 而作用域指出了从用户代码的哪些物理位置可以访问到这些名字。

它们的关系如下图:
命名空间和变量作用域

每个名称空间是一个自我包含的单元,但从作用域的观点来看, 事情是不同的:所有局部名称空间的名称都在局部作用范围内,局部作用范围以外的所有名称都在全局作用范围内。

(3)名称查找

那么确定作用域的规则是如何联系到名称空间的呢?
它所要做的就是名称查询. 访问一个属性时, 解释器必须在三个名称空间中的一个找到它。 首先从局部名称空间开始, 如果没有找到, 解释器将继续查找全局名称空间. 如果这也失败了, 它将在内建名称空间里查找。

(4)无限制的命名空间

Python 的一个有用的特性在于你可以在任何需要放置数据的地方获得一个名称空间。我们可以在任何时候给函数、对象添加属性:

code example

>>> def foo():
        pass

>>> foo.haha = 'haha'
>>> foo
<function foo at 0x02C05AF0>
>>> foo.haha
'haha'

可以把任何想要的东西放入一个名称空间里。

3.导入模块

(1)import 语句的模块顺序

推荐所有的模块在 Python 模块的开头部分导入。 而且最好按照这样的顺序:

  • Python 标准库模块
  • Python 第三方模块
  • 应用程序自定义模块

然后使用一个空行分割这三类模块的导入语句。

(2)导入和加载关系

解释器执行到import语句, 如果在搜索路径中找到了指定的模块, 就会加载它。如果模块是被第一次导入, 它将被加载并执行。

该过程遵循作用域原则, 如果在一个模块的顶层导入, 那么它的作用域就是全局的; 如果在函数中导入, 那么它的作用域是局部的。

(3)多行导入

提倡下面这种风格:

code example

from Tkinter import Tk, Frame, Button, Entry, Canvas, Text
from Tkinter import LEFT, DISABLED, NORMAL, RIDGE, END

4.模块导入的特性

(1)载入时执行模块

加载模块会导致这个模块被”执行”。 也就是被导入模块的顶层代码将直接被执行。 这通常包括设定全局变量以及类和函数的声明。 如果有检查 __name__ 的操作, 那么它也会被执行。

你应该把尽可能多的代码封装到函数。 明确地说,只把函数和模块定义放入模块的顶层是良好的模块编程习惯。

(2)导入和加载

一个模块只被加载一次, 无论它被导入多少次。 这可以阻止多重导入时代码被多次执行。 例如你的模块导入了 sys 模块, 而你要导入的其他 5 个模块也导入了它, 那么每次都加载 sys (或是其他模块)不是明智之举! 所以, 加载只在第一次导入时发生。

(3)限制使用from module import *

在实践中, 我们认为 from module import * 不是良好的编程风格, 因为它”污染”当前名称空间, 而且很可能覆盖当前名称空间中现有的名字; 但如果某个模块有很多要经常访问的变量或者模块的名字很长, 这也不失为一个方便的好办法。
我们只在两种场合下建议使用这样的方法, 一个场合是:目标模块中的属性非常多, 反复键入模块名很不方便, 例如 TkinterNumPy模块, 可能还有socket 模块。另一个场合是在交互解释器下, 因为这样可以减少输入次数。

(4)导入的变量是局部拷贝

对这些变量的改变只影响它的局部拷贝而不是所导入模块的原始名称空间。

一个例子
这里我们提供了两个模块的代码: 一个导入者, impter.py , 一个被导入者, imptee.pyimpter.py 使用 from-import 语句只创建了局部绑定。

#############
# imptee.py #
#############
foo = 'abc'
def show():
    print 'foo from imptee:', foo


#############
# impter.py #
#############
from imptee import foo, show
show()
foo = 123
print 'foo from impter:', foo
show()

output

foo from imptee: abc
foo from impter: 123
foo from imptee: abc

分析:从imptee import 进来的fooimpteefoo的一份拷贝。所以你改变它的值,原始模块里(imptee)的foo不会改变。

如果你想导入的是原始变量(非拷贝),那得使用.操作符。

code

#############
# impter.py #
#############
import imptee
imptee.show()
imptee.foo = 123
print 'foo from impter:', imptee.foo
imptee.show()

output

foo from imptee: abc
foo from impter: 123
foo from imptee: 123

5.模块内建函数

(1)__import__()

此函数是模块导入函数。

import sys语句等价于sys = __import__('sys')

(2)globals()locals()

globals()locals() 内建函数分别返回调用者全局和局部名称空间的字典。

(3)reload()

reload()内建函数可以重新导入一个已经导入的模块。

6.模块其他特性

(1)模块自动载入

当 Python 解释器在标准模式下启动时, 一些模块会被解释器自动导入, 用于系统相关操作。比如__builtin__ 模块, 它会正常地被载入。

sys.modules 变量包含一个由当前载入(完整且成功导入)到解释器的模块组成的字典, 模块名作为键, 它们的位置作为值。

>>> import sys
>>> len(sys.modules.keys())
218
>>> sys.modules.keys()[:10]
['heapq', 'code', 'tkFileDialog', 'functools', 'random', 'ctypes.os', 'sysconfig', 'idlelib.macosxSupport', 'idlelib.PyParse', 'tkSimpleDialog']

(2)阻止属性的导入

如果不想模块内的某个属性被import,可以在属性名前加_

code example

# haha.py
ha1 = 5
ha2 = 6
_ha2 = 7
>>> from haha import *
>>> ha1
5
>>> ha2
6
>>> _ha2

Traceback (most recent call last):
  File "<pyshell#24>", line 1, in <module>
    _ha2
NameError: name '_ha2' is not defined

可以看到,_ha2属性没有被导入进来。

(3)导入循环

问题起因

看一个例子
a.py中有一个函数a(),需要调用b.py中的函数b(), 而b.py中的函数c()又需要调用a(),这就出现了循环导入。

# a.py
from b import b
def a():
    print "hello, a"   
    b()

a()
# b.py
from a import a
def b():
    print "hello, b"

def c():
    a()

c()

运行a.py,报错如下:

C:\Users\wangjiang\Desktop>python a.py
Traceback (most recent call last):
  File "a.py", line 2, in <module>
    from b import b
  File "C:\Users\wangjiang\Desktop\b.py", line 2, in <module>
    from a import a
  File "C:\Users\wangjiang\Desktop\a.py", line 2, in <module>
    from b import b
ImportError: cannot import name b

a.py中导入b.b(),在导入b文件的时候,又要去导入a文件,a文件又要去导入b文件,这是一个死循环了,自然是不允许的。


解决方案

1.将导入模块的语句放在局部(函数)里。如下所示:

# a.py
def a():
    from b import b
    print "hello, a"   
    b()

a()
# b.py
def b():
    print "hello, b"

def c():
    from a import a
    a()

c()
C:\Users\wangjiang\Desktop>python a.py
hello, a
hello, b
hello, a
hello, b
hello, a
hello, b

2.重构代码
出现循环import的问题往往意味着代码的设计有问题




Ref

《Python核心编程》
python 导入循环问题
解决循环import的问题

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值