包 包是一種通過使用‘.模塊名’來組織python模塊名稱空間的方式。 1. 無論是import形式還是from...import形式,凡是在導入語句中(而不是在使用時)遇到帶點的,都要第一時間提高警覺:這是關於包才有的導入語法 2. 包是目錄級的(文件夾級),文件夾是用來組成py文件(包的本質就是 ...
包
包是一種通過使用‘.模塊名’來組織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)
glance/ #Top-level package
├── __init__.py #Initialize the glance package
├── api #Subpackage for api
│ ├── __init__.py
│ ├── policy.py
│ └── versions.py
├── cmd #Subpackage for cmd
│ ├── __init__.py
│ └── manage.py
└── db #Subpackage for db
├── __init__.py
└── models.py
#文件內容
#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那必須使用後者。
import
我們在與包glance同級別的文件中測試
1 import glance.db.models
2 glance.db.models.register_models('mysql')
from...import...
需要註意的是from後import導入的模塊,必須是明確的一個不能帶點,否則會有語法錯誤,如:from a import b.c是錯誤語法
我們在與包glance同級別的文件中測試
1 from glance.db import models
2 models.register_models('mysql')
3
4 from glance.db.models import register_models
5 register_models('mysql')
__init__.py文件
不管是哪種方式,只要是第一次導入包或者是包的任何其他部分,都會依次執行包下的__init__.py文件(我們可以在每個包的文件內都列印一行內容來驗證一下),這個文件可以為空,但是也可以存放一些初始化包的代碼。
from galnce.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仍然不能導入)。
glance/
├── __init__.py
├── api
│ ├── __init__.py __all__ = ['policy','versions']
│ ├── policy.py
│ └── versions.py
├── cmd __all__ = ['manage']
│ ├── __init__.py
│ └── manage.py
└── db __all__ = ['models']
├── __init__.py
└── models.py
from glance.api import *
policy.get()
絕對導入和相對導入
我們的最頂級包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同級的文件中測試
1 from glance.api import versions
註意:在使用pycharm時,有的情況會為你多做一些事情,這是軟體相關的東西,會影響你對模塊導入的理解,因而在測試時,一定要回到命令行去執行,模擬我們生產環境,你總不能拿著pycharm去上線代碼吧!!!
特別需要註意的是:可以用import導入內置或者第三方模塊(已經在sys.path中),但是要絕對避免使用import來導入自定義包的子模塊(沒有在sys.path中),應該使用from... import ...的絕對或者相對導入,且包的相對導入只能用from的形式。
比如我們想在glance/api/versions.py中導入glance/api/policy.py,有的同學一抽這倆模塊是在同一個目錄下,十分開心的就去做了,它直接這麼做
1 #在version.py中
2
3 import policy
4 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,
這必然是找不到的
'''
glance/
├── __init__.py from glance import api
from glance import cmd
from glance import db
├── api
│ ├── __init__.py from glance.api import policy
from glance.api import versions
│ ├── policy.py
│ └── versions.py
├── cmd from glance.cmd import manage
│ ├── __init__.py
│ └── manage.py
└── db from glance.db import models
├── __init__.py
└── models.py
glance/
├── __init__.py from . import api #.表示當前目錄
from . import cmd
from . import db
├── api
│ ├── __init__.py from . import policy
from . import versions
│ ├── policy.py
│ └── versions.py
├── cmd from . import manage
│ ├── __init__.py
│ └── manage.py from ..api import policy
#..表示上一級目錄,想再manage中使用policy中的方法就需要回到上一級glance目錄往下找api包,從api導入policy
└── db from . import models
├── __init__.py
└── models.py
單獨導入包
單獨導入包名稱時不會導入包中所有包含的所有子模塊,如
#在與glance同級的test.py中
import glance
glance.cmd.manage.main()
'''
執行結果:
AttributeError: module 'glance' has no attribute 'cmd'
'''
解決方法:
1 #glance/__init__.py
2 from . import cmd
3
4 #glance/cmd/__init__.py
5 from . import manage
執行:
1 #在於glance同級的test.py中
2 import glance
3 glance.cmd.manage.main()
千萬別問:__all__不能解決嗎,__all__是用於控制from...import *
import glance之後直接調用模塊中的方法
glance/
├── __init__.py from .api import *
from .cmd import *
from .db import *
├── api
│ ├── __init__.py __all__ = ['policy','versions']
│ ├── policy.py
│ └── versions.py
├── cmd __all__ = ['manage']
│ ├── __init__.py
│ └── manage.py
└── db __all__ = ['models']
├── __init__.py
└── models.py
import glance
policy.get()
軟體開發規範

#=============>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目錄:存放日誌 #all2.log [2017-07-29 00:31:40,272][MainThread:11692][task_id:conf.my_log_settings][my_log_settings.py:75][INFO][It works!] [2017-07-29 00:31:41,789][MainThread:11692][task_id:core.core][core.py:25][ERROR][用戶名不存在] [2017-07-29 00:31:46,394][MainThread:12348][task_id:conf.my_log_settings][my_log_settings.py:75][INFO][It works!] [2017-07-29 00:31:47,629][MainThread:12348][task_id:core.core][core.py:25][ERROR][用戶名不存在] [2017-07-29 00:31:57,912][MainThread:10528][task_id:conf.my_log_settings][my_log_settings.py:75][INFO][It works!] [2017-07-29 00:32:03,340][MainThread:12744][task_id:conf.my_log_settings][my_log_settings.py:75][INFO][It works!] [2017-07-29 00:32:05,065][MainThread:12916][task_id:conf.my_log_settings][my_log_settings.py:75][INFO][It works!] [2017-07-29 00:32:08,181][MainThread:12916][task_id:core.core][core.py:25][ERROR][用戶名不存在] [2017-07-29 00:32:13,638][MainThread:7220][task_id:conf.my_log_settings][my_log_settings.py:75][INFO][It works!] [2017-07-29 00:32:23,005][MainThread:7220][task_id:core.core][core.py:20][INFO][登錄成功] [2017-07-29 00:32:40,941][MainThread:7220][task_id:core.core][core.py:20][INFO][登錄成功] [2017-07-29 00:32:47,222][MainThread:7220][task_id:core.core][core.py:20][INFO][登錄成功] [2017-07-29 00:32:51,949][MainThread:7220][task_id:core.core][core.py:25][ERROR][用戶名不存在] [2017-07-29 00:33:00,213][MainThread:7220][task_id:core.core][core.py:20][INFO][登錄成功] [2017-07-29 00:33:50,118][MainThread:8500][task_id:conf.my_log_settings][my_log_settings.py:75][INFO][It works!] [2017-07-29 00:33:55,845][MainThread:8500][task_id:core.core][core.py:20][INFO][登錄成功] [2017-07-29 00:34:06,837][MainThread:8500][task_id:core.core][core.py:25][ERROR][用戶名不存在] [2017-07-29 00:34:09,405][MainThread:8500][task_id:core.core][core.py:25][ERROR][用戶名不存在] [2017-07-29 00:34:10,645][MainThread:8500][task_id:core.core][core.py:25][ERROR][用戶名不存在]View Code