一. 模块
1. 概念
为了便于代码维护,一般很多函数分组,分别放到不同的文件里,每个文件包含的代码就相对较少,维护也更方便。在Python中,一个.py文件就称之为一个模块(Module)。
Python中模块分为三种: Python标准库,第三方模块,应用程序自定义模块
使用模块可以帮助避免函数与其他模块函数重名,函数名与变量名重名的问题。
此外,要尽量避免定义的模块名与内置函数(build in function)重名。
2. 模块引入方法
模块的引入使用import
相关语句。
在模块中可以包含可执行的语句和函数的定义,这些语句的目的是初始化模块,它们只在模块名第一次遇到导入import语句时才执行。
import语句是可以在程序中的任意位置使用,同一个模块可以被多次引用
为了防止在同一个文件中重复引入,python使用了一下优化方法:第一次导入后将模块名加载到内存,后续的import
语句仅是对已经加载到内存中的模块对象增加了一次引用,不会重新执行模块内的语句
import 语句
import module1[, module2[, module3[, ...moduleN]]]
Python解释器通过import 语句
在搜索路径中搜索相应的模块,
模块的查找顺序是:内存中已经加载的模块->内置模块->sys.path路径中包含的模块
若在当前目录下存在与要引入模块同名的文件,就会把要引入的模块屏蔽掉
from ... import 语句
from modulename import name1[, name2[, ... nameN]]
这个声明不会把整个modulename
模块导入到当前的命名空间中,只会将它里面的name1
或name2
单个引入到执行这个声明的模块的全局符号表
from ... import *
from modulename import *
通过使用*
提供了一个简单的方法来导入一个模块中的所有项目。然而这种声明不该被过多地使用。大多数情况, Python程序员不使用这种方法,因为引入的其它来源的命名,很可能覆盖了已有的定义。
3. 为模块名起别名
别名的使用可以简化编程
两个模块xmlreader.py
和csvreader.py
,它们都定义了函数read_data(filename)
,用来从文件中读取一些数据,但采用不同的输入格式。可以通过为模块起别名实现使用同样的代码实现不同的功能,例如
if file_format == 'xml':
import xmlreader as reader
elif file_format == 'csv':
import csvreader as reader
data=reader.read_date(filename)
二. 包
为了避免模块名冲突,Python引入了按目录来组织模块的方法,称为包(Package)。
无论是import
形式还是from...import
,凡是在导入语句中(而不是在使用时)遇到带点的,都要第一时间提高警觉:这是关于包才有的导入语法,点的左边都必须是一个包。
包的本质就是一个包含__init__.py
文件的目录。
需要注意的是from后import导入的模块不能带点,否则会有语法错误,如:from a import b.c
是错误语法
1. __init__.py
无论使用import
还是from import
,只要是第一次导入包或者是包的任何其他部分,都会依次执行包下的__init__.py
文件。
2. from modulename import *
Foo #Top-level package
|__ __init__.py #Initialize the glance package
|
|__ api #Subpackage for api
| |__ __init__.py
| |__ read.py
| |__ write.py
|
|__ cmd #Subpackage for cmd
| |__ __init__.py
| |__ control.py
|
|__ db #Subpackage for db
|__ __init__.py
|__ data.py
此处是想从包api中导入所有,实际上该语句只会导入包api下__init__.py文件中定义的名字,我们可以在这个文件中定义__all___:
#在__init__.py中定义
x=10
def func():
print('from api.__init.py')
__all__=['x','read', 'func']
此时在与Foo同级的文件中执行from Foo.api import *就导入__all__中的内容,write仍然不能导入。
3. 绝对导入和相对导入
上述例子中最顶级包Foo是写给别人用的,然后在Foo包内部也会有彼此之间互相导入的需求,这时候就有绝对导入和相对导入两种方式:
绝对导入:以Foo作为起始
相对导入:用.或者..的方式最为起始(只能在一个包中使用,不能用于不同目录内)
例如:我们在Foo/api/write.py中想要导入Foo/cmd/control.py
在Foo/api/write.py
#绝对导入
from Foo.cmd import control
control.ctrl()
#相对导入
from ..cmd import control
control.ctrl()
特别需要注意的是:可以用import导入内置或者第三方模块,但是要绝对避免使用import来导入自定义包的子模块,应该使用from... import ...的绝对或者相对导入,且包的相对导入只能用from的形式。
__all__是用于控制from...import * ,不能用于使用import单独导入包
4. 多层目录直接调用内部函数
创建如下目录结构
Foo
|---test.py
|---keystone
|---auth
| |---plugins
| | |---core.py
| | |---__init__.py
| |
| |---__init__.py
|
|---__init__.py
core.py内容为:
def create(cls=None, auth_payload=None, method_name=None):
print('function create')
pass
class UserAuthInfo:
def __init__(self):
self.password = None
def foo(self):
print('cls UserAuthInfo')
要求:import keystone,然后就可以直接调用keystone.create和keystone.UserAuthInfo
在keystone目录下的__init__.py
中输入如下代码
from .auth.plugins.core import create
from .auth.plugins.core import UserAuthInfo
在test.py中调用core.py中的函数
import keystone
keystone.create()
a = keystone.UserAuthInfo()
a.foo()
>>function create
>>cls UserAuthInfo
5. 包与包之间模块导入
目录结构及调用关系如下所示
Foo
|---dir1
| |---hello.py
|---dir2
|---main.py
其中,hello.py:
def add(x,y)
return x+y
main.py如何能调用到hello.py中的add函数。
解决方法
import os
import sys
BASE_DIR = os.path.dirname(os.path.dirname(__file__)) # 当前project的目录
print(BASE_DIR)
sys.path.append(BASE_DIR) # 添加当前project目录到环境变量
from dir1 import hello
print(hello.add(1, 2))
网友评论