模块和包
1.模块
re模块
collections模块
time模块
random模块
os模块和sys模块
序列化模块
1.模块的导入
所有的模块导入都应该尽量往上写。
- 内置模块
- 扩展模块(django)
- 自定义模块
模块不会重复被导入 : sys.moudles
从哪儿导入模块 : sys.path
通过import导入所需要的模块。
import 模块名
模块名.变量名 和本文件中的变量名完全不冲突。
import 模块名 as 重命名的模块名 : 提高代码的兼容性。
import 模块1,模块2
import time,sys,os
1>from import
2>from 模块名 import 变量名
直接使用变量名就可以完成操作,如果本文件中有相同的变量名会发生冲突。
from time import sleep from demo import read def read(): print('my read') read()
3>from 模块名 import 变量名字 as 重命名变量名
import time as t print(t.time())
4>from 模块名 import 变量名1,变量名2
import demo from demo import 变量名 from demo import money,read print(money) read() money = 200 read()
5>from 模块名 import *
将模块中的所有变量名都放到内存中,如果本文件中有相同的变量名会发生冲突。
from time import * sleep = 10 sleep(1) #sleep与time内部所定义的函数出现冲突,导致内部函数不可用。
#导致内部变量不可用 from math import pi print(pi) pi = 3 print(pi)
6>from 模块名 import * 和 __all__ 是一对
没有这个变量,就会导入所有的名字,如果有all 只导入all列表中的名字。
from demo import * print(money) read() #通过上述导入,使得在使用模块内的变量时,直接应用变量名即可。 import demo print(demo.money) #通过上述导入,使得在使用模块内的变量时,直接(模块.变量名)即可
7>python导入模块时,为什么导入多次并不发生冲突,还能保证程序正常进行?
它的处理机制如下:
先从sys.modules里查看是否已经被导入,如果没有被导入,就依据sys.path路径取寻找模块,找到了就导入,创建这个模块的命名空间,执行文件,把文件中的名字都放到命名空间里。
8>__name__
在模块中 有一个变量__name__,
当我们直接执行这个模块的时候,__name__ == '__main__'
当我们执行其他模块,在其他模块中引用这个模块的时候,这个模块中的__name__ == '模块的名字'
import sys print(sys.modules.keys()) print(sys.path)
模块文件
__all__ = ['read','read2'] money = 100 def login(): print('正在执行登录程序') def read(): print('in read1',money) def read2(): print('in read2') if __name__ == '__main__': ret = login() print(ret)
执行文件
import demo def read(): print('my read func') demo.read() print(demo.money)
通过下面方法可以提高程序兼容性,可移植性。
# oracle、mysql if 数据库 == ‘oracle’: import oracle as db elif 数据库 == ‘mysql’: import mysql as db
2.包
包是一种通过使用‘.模块名’来组织python模块名称空间的方式。
1.无论是import形式还是from...import形式,凡是在导入语句中(而不是在使用时)遇到带点的,都要第一时间提高警觉:这是关于包才有的导入语法。
2.包是目录级的(文件夹级),文件夹是用来组成py文件(包的本质就是一个包含__init__.py文件的目录)。
3.import导入文件时,产生名称空间中的名字来源于文件,import 包,产生的名称空间的名字同样来源于文件,即包下的__init__.py,导入包本质就是在导入该文件。
强调:
1. 在python3中,即使包下没有__init__.py文件,import 包仍然不会报错,而在python2中,包下一定要有该文件,否则import 包报错。
2. 创建包的目的不是为了运行,而是被导入使用,记住,包只是模块的一种形式而已,包即模块。
包A和包B下有同名模块也不会冲突,如A.a与B.a来自俩个命名空间:
import os os.makedirs('glance/api') os.makedirs('glance/cmd') os.makedirs('glance/db') l = [] l.append(open('glance/__init__.py','w')) l.append(open('glance/api/__init__.py','w')) l.append(open('glance/api/policy.py','w')) l.append(open('glance/api/versions.py','w')) l.append(open('glance/cmd/__init__.py','w')) l.append(open('glance/cmd/manage.py','w')) l.append(open('glance/db/models.py','w')) map(lambda f:f.close() ,l)
#文件内容 #policy.py def get(): print('from policy.py') #versions.py def create_resource(conf): print('from version.py: ',conf) #manage.py def main(): print('from manage.py') #models.py def register_models(engine): print('from models.py: ',engine)
注意事项:
1.关于包相关的导入语句也分为import和from ... import ...两种,但是无论哪种,无论在什么位置,在导入时都必须遵循一个原则:凡是在导入时带点的,点的左边都必须是一个包,否则非法。可以带有一连串的点,如item.subitem.subsubitem,但都必须遵循这个原则。
2.对于导入后,在使用时就没有这种限制了,点的左边可以是包,模块,函数,类(它们都可以用点的方式调用自己的属性)。
3.对比import item 和from item import name的应用场景:如果我们想直接使用name,那必须使用后者。
2.1 import
我们在与包glance同级别的文件中测试。
import glance.db.models glance.db.models.register_models('mysql')
2.2 from……import……
需要注意的是from后import导入的模块,必须是明确的一个不能带点,否则会有语法错误,如:from a import b.c是错误语法。
我们在与包glance同级别的文件中测试。
from glance.db import models models.register_models('mysql') from glance.db.models import register_models register_models('mysql')
2.3 __init__.py文件
不管是哪种方式,只要是第一次导入包或者是包的任何其他部分,都会依次执行包下的__init__.py文件(我们可以在每个包的文件内都打印一行内容来验证一下),这个文件可以为空,但是也可以存放一些初始化包的代码。
2.4 from glance.api import *
在讲模块时,我们已经讨论过了从一个模块内导入所有*,此处我们研究从一个包导入所有*。
此处是想从包api中导入所有,实际上该语句只会导入包api下__init__.py文件中定义的名字,我们可以在这个文件中定义__all___,它定义了所有需要导入的文件名参数及函数:
#在__init__.py中定义 x=10 def func(): print('from api.__init.py') __all__=['x','func','policy']
此时我们在与glance同级的文件中执行from glance.api import *就导入__all__中的内容(versions仍然不能导入)。
2.5 绝对导入和相对导入
我们的最顶级包glance是写给别人用的,然后在glance包内部也会有彼此之间互相导入的需求,这时候就有绝对导入和相对导入两种方式:
绝对导入:以glance作为起始
相对导入:用.或者..的方式最为起始(只能在一个包中使用,不能用于不同目录内)
例如:我们在glance/api/version.py中想要导入glance/cmd/manage.py
#在glance/api/version.py #绝对导入 from glance.cmd import manage manage.main() #相对导入 from ..cmd import manage manage.main()
注意:一定要在于glance同级的文件中测试。
from glance.api import versions
from glance.api import versions versions.manage.main()
特别需要注意的是:可以用import导入内置或者第三方模块(已经在sys.path中),但是要绝对避免使用import来导入自定义包的子模块(没有在sys.path中),应该使用from... import ...的绝对或者相对导入,且包的相对导入只能用from的形式。
#在version.py中 import policy policy.get()
没错,我们单独运行version.py是一点问题没有的,运行version.py的路径搜索就是从当前路径开始的,于是在导入policy时能在当前目录下找到。
但是子包中的模块version.py极有可能是被一个glance包同一级别的其他文件导入,比如我们在于glance同级下的一个test.py文件中导入version.py,如下:
from glance.api import versions ''' 执行结果: ImportError: No module named 'policy' ''' 分析: 此时我们导入versions在versions.py中执行,import policy需要找从sys.path也就是从当前目录找policy.py,这必然是找不到的。
2.7 单独导入包
单独导入包名称时不会导入包中所有包含的所有子模块,如:
#在与glance同级的test.py中 import glance glance.cmd.manage.main() ''' 执行结果: AttributeError: module 'glance' has no attribute 'cmd' '''
通过在glance包下的__init__.py文件中设置,就可以实现。如果还需要深层次的调用,那也需要深层次的在__init__.py文件中导入。代码如下:
#glance/__init__.py 2 from . import cmd 3 4 #glance/cmd/__init__.py 5 from . import manage
注意:__all__是用于控制from...import *。
相对和绝对路径的区别:
1.绝对路径:不管在包内部还是外部,导入了就能用。不能挪动,但是直观。
2.相对路径:可以随意移动包,只要能找到包的位置,就可以使用包里的模块,包里的模块如果想使用其它模块的内容只能使用相对路径,使用了相对路径就不能在包内直接执行了。
软件开发规范
#=============>bin目录:存放执行脚本 #start.py import sys,os BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(BASE_DIR) from core import core from conf import my_log_settings if __name__ == '__main__': my_log_settings.load_my_logging_cfg() core.run() #=============>conf目录:存放配置文件 #config.ini [DEFAULT] user_timeout = 1000 [egon] password = 123 money = 10000000 [alex] password = alex3714 money=10000000000 [yuanhao] password = ysb123 money=10 #settings.py import os config_path=r'%s\%s' %(os.path.dirname(os.path.abspath(__file__)),'config.ini') user_timeout=10 user_db_path=r'%s\%s' %(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),\ 'db') #my_log_settings.py """ logging配置 """ import os import logging.config # 定义三种日志输出格式 开始 standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \ '[%(levelname)s][%(message)s]' #其中name为getlogger指定的名字 simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s' id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s' # 定义日志输出格式 结束 logfile_dir = r'%s\log' %os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # log文件的目录 logfile_name = 'all2.log' # log文件名 # 如果不存在定义的日志目录就创建一个 if not os.path.isdir(logfile_dir): os.mkdir(logfile_dir) # log文件的全路径 logfile_path = os.path.join(logfile_dir, logfile_name) # log配置字典 LOGGING_DIC = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'standard': { 'format': standard_format }, 'simple': { 'format': simple_format }, }, 'filters': {}, 'handlers': { #打印到终端的日志 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', # 打印到屏幕 'formatter': 'simple' }, #打印到文件的日志,收集info及以上的日志 'default': { 'level': 'DEBUG', 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件 'formatter': 'standard', 'filename': logfile_path, # 日志文件 'maxBytes': 1024*1024*5, # 日志大小 5M 'backupCount': 5, 'encoding': 'utf-8', # 日志文件的编码,再也不用担心中文log乱码了 }, }, 'loggers': { #logging.getLogger(__name__)拿到的logger配置 '': { 'handlers': ['default', 'console'], # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕 'level': 'DEBUG', 'propagate': True, # 向上(更高level的logger)传递 }, }, } def load_my_logging_cfg(): logging.config.dictConfig(LOGGING_DIC) # 导入上面定义的logging配置 logger = logging.getLogger(__name__) # 生成一个log实例 logger.info('It works!') # 记录该文件的运行状态 if __name__ == '__main__': load_my_logging_cfg() #=============>core目录:存放核心逻辑 #core.py import logging import time from conf import settings from lib import read_ini config=read_ini.read(settings.config_path) logger=logging.getLogger(__name__) current_user={'user':None,'login_time':None,'timeout':int(settings.user_timeout)} def auth(func): def wrapper(*args,**kwargs): if current_user['user']: interval=time.time()-current_user['login_time'] if interval < current_user['timeout']: return func(*args,**kwargs) name = input('name>>: ') password = input('password>>: ') if config.has_section(name): if password == config.get(name,'password'): logger.info('登录成功') current_user['user']=name current_user['login_time']=time.time() return func(*args,**kwargs) else: logger.error('用户名不存在') return wrapper @auth def buy(): print('buy...') @auth def run(): print(''' 购物 查看余额 转账 ''') while True: choice = input('>>: ').strip() if not choice:continue if choice == '1': buy() if __name__ == '__main__': run() #=============>db目录:存放数据库文件 #alex_json #egon_json #=============>lib目录:存放自定义的模块与包 #read_ini.py import configparser def read(config_file): config=configparser.ConfigParser() config.read(config_file) return config #=============>log目录:存放日志
浙公网安备 33010602011771号