1. 程式人生 > >python:包與異常處理

python:包與異常處理

word 要求 所有 expect return interrupt itl 標識 toolbar

一、包

1,什麽是包?

把解決一類問題的模塊放在同一個文件夾裏-----包

2,包是一種通過使用‘.模塊名’來組織python模塊名稱空間的方式。

  1. 無論是import形式還是from...import形式,凡是在導入語句中(而不是在使用時)遇到帶點的,都要第一時間提高警覺:這是關於包才有的導入語法

  2. 包是目錄級的(文件夾級),文件夾是用來組成py文件(包的本質就是一個包含__init__.py文件的目錄)

  3. import導入文件時,產生名稱空間中的名字來源於文件,import 包,產生的名稱空間的名字同樣來源於文件,即包下的__init__.py,導入包本質就是在導入該文件

3,強調:

  1. 在python3中,即使包下沒有__init__.py文件,import 包仍然不會報錯,而在python2中,包下一定要有該文件,否則import 包報錯

  2. 創建包的目的不是為了運行,而是被導入使用,包只是模塊的一種形式而已,包即模塊

4,註意事項

  1.關於包相關的導入語句也分為import和from ... import ...兩種,但是無論哪種,無論在什麽位置,在導入時都必須遵循一個原則:凡是在導入時帶點的,點的左邊都必須是一個包,否則非法。可以帶有一連串的點,如item.subitem.subsubitem,但都必須遵循這個原則。

  2.對於導入後,在使用時就沒有這種限制了,點的左邊可以是包,模塊,函數,類(它們都可以用點的方式調用自己的屬性)。

  3.對比import item 和from item import name的應用場景:
  如果我們想直接使用name那必須使用後者。

技術分享圖片
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 目錄結構
目錄結構

5,import

我們在與包glance同級別的文件中測試

1 import glance.db.models
2 glance.db.models.register_models(mysql) 

6,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)

7,__init__.py文件

不管是哪種方式,只要是第一次導入包或者是包的任何其他部分,都會依次執行包下的__init__.py文件(我們可以在每個包的文件內都打印一行內容來驗證一下),這個文件可以為空,但是也可以存放一些初始化包的代碼。

8,絕對導入和相對導入

最頂級包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()

特別需要註意的是:可以用import導入內置或者第三方模塊(已經在sys.path中),但是要絕對避免使用import來導入自定義包的子模塊(沒有在sys.path中),應該使用from... import ...的絕對或者相對導入,且包的相對導入只能用from的形式。

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

相對導入
相對導入

9,單獨導入包

1 #glance/__init__.py
2 from . import cmd
3 
4 #glance/cmd/__init__.py
5 from . import manage

10,軟件開發規範

技術分享圖片

技術分享圖片
#=============>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

二、異常處理

1,程序中難免出現錯誤,而錯誤分成兩種:

  1.語法錯誤(這種錯誤,根本過不了python解釋器的語法檢測,必須在程序執行前就改正)

技術分享圖片
#語法錯誤示範一
if

#語法錯誤示範二
def test:
    pass

#語法錯誤示範三
print(haha

語法錯誤
語法錯誤

  2.邏輯錯誤(邏輯錯誤)

技術分享圖片
#用戶輸入不完整(比如輸入為空)或者輸入非法(輸入不是數字)
num=input(">>: ")
int(num)

#無法完成計算
res1=1/0
res2=1+str
邏輯錯誤

2,什麽是異常

異常就是程序運行時發生錯誤的信號

  技術分享圖片

3,python中的異常種類

 在python中不同的異常可以用不同的類型(python中統一了類與類型,類型即類)去標識,不同的類對象標識不同的異常,一個異常標識一種錯誤

  

技術分享圖片
l=[egon,aa]
l[3]
Index Error 技術分享圖片
dic={name:egon}
dic[age]
Key Error 技術分享圖片
s=hello
int(s)
Value Error

4,常用異常

技術分享圖片
AttributeError 試圖訪問一個對象沒有的樹形,比如foo.x,但是foo沒有屬性x
IOError 輸入/輸出異常;基本上是無法打開文件
ImportError 無法引入模塊或包;基本上是路徑問題或名稱錯誤
IndentationError 語法錯誤(的子類) ;代碼沒有正確對齊
IndexError 下標索引超出序列邊界,比如當x只有三個元素,卻試圖訪問x[5]
KeyError 試圖訪問字典裏不存在的鍵
KeyboardInterrupt Ctrl+C被按下
NameError 使用一個還未被賦予對象的變量
SyntaxError Python代碼非法,代碼不能編譯(個人認為這是語法錯誤,寫錯了)
TypeError 傳入對象類型與要求的不符合
UnboundLocalError 試圖訪問一個還未被設置的局部變量,基本上是由於另有一個同名的全局變量,
導致你以為正在訪問它
ValueError 傳入一個調用者不期望的值,即使值的類型是正確的

常用異常
常用異常

5,更多異常

技術分享圖片
ArithmeticError
AssertionError
AttributeError
BaseException
BufferError
BytesWarning
DeprecationWarning
EnvironmentError
EOFError
Exception
FloatingPointError
FutureWarning
GeneratorExit
ImportError
ImportWarning
IndentationError
IndexError
IOError
KeyboardInterrupt
KeyError
LookupError
MemoryError
NameError
NotImplementedError
OSError
OverflowError
PendingDeprecationWarning
ReferenceError
RuntimeError
RuntimeWarning
StandardError
StopIteration
SyntaxError
SyntaxWarning
SystemError
SystemExit
TabError
TypeError
UnboundLocalError
UnicodeDecodeError
UnicodeEncodeError
UnicodeError
UnicodeTranslateError
UnicodeWarning
UserWarning
ValueError
Warning
ZeroDivisionError

更多異常
更多異常

三、異常處理

1,什麽是異常?

異常發生之後

異常之後的代碼就不執行了

2,什麽是異常處理

python解釋器檢測到錯誤,觸發異常(也允許程序員自己觸發異常)

程序員編寫特定的代碼,專門用來捕捉這個異常(這段代碼與程序邏輯無關,與異常處理有關)

如果捕捉成功則進入另外一個處理分支,執行你為其定制的邏輯,使程序不會崩潰,這就是異常處理

3,如何進行異常處理?

首先須知,異常是由程序的錯誤引起的,語法上的錯誤跟異常處理無關,必須在程序運行前就修正

一: 使用if判斷式

正常的計劃:

num1=input(>>: ) #輸入一個字符串試試
int(num1)
技術分享圖片
#_*_coding:utf-8_*_
__author__ = Linhaifeng

num1=input(>>: ) #輸入一個字符串試試
if num1.isdigit():
    int(num1) #我們的正統程序放到了這裏,其余的都屬於異常處理範疇
elif num1.isspace():
    print(輸入的是空格,就執行我這裏的邏輯)
elif len(num1) == 0:
    print(輸入的是空,就執行我這裏的邏輯)
else:
    print(其他情情況,執行我這裏的邏輯)

‘‘‘
問題一:
使用if的方式我們只為第一段代碼加上了異常處理,但這些if,跟你的代碼邏輯並無關系,這樣你的代碼會因為可讀性差而不容易被看懂

問題二:
這只是我們代碼中的一個小邏輯,如果類似的邏輯多,那麽每一次都需要判斷這些內容,就會倒置我們的代碼特別冗長。
‘‘‘

使用if判斷進行異常處理
使用if判斷進行異常處理

總結:

1.if判斷式的異常處理只能針對某一段代碼,對於不同的代碼段的相同類型的錯誤你需要寫重復的if來進行處理。

2.在你的程序中頻繁的寫與程序本身無關,與異常處理有關的if,會使得你的代碼可讀性極其的差

3.if是可以解決異常的,只是存在1,2的問題,所以,千萬不要妄下定論if不能用來異常處理。

二:python為每一種異常定制了一個類型,然後提供了一種特定的語法結構用來進行異常處理

part1:基本語法

try:
     被檢測的代碼塊
except 異常類型:
     try中一旦檢測到異常,就執行這個位置的邏輯
技術分享圖片
f = open(a.txt)

g = (line.strip() for line in f)
for line in g:
    print(line)
else:
    f.close()
讀文件例1 技術分享圖片讀文件例2

part2:異常類只能用來處理指定的異常情況,如果非指定異常則無法處理。

# 未捕獲到異常,程序直接報錯
 
s1 = hello
try:
    int(s1)
except IndexError as e:
    print e

part3:多分支

s1 = hello
try:
int(s1)
except IndexError as e:
print(e)
except KeyError as e:
print(e)
except ValueError as e:
print(e)

part4:萬能異常 在python的異常中,有一個萬能異常:Exception,他可以捕獲任意異常,即:

s1 = ‘hello‘
try:
    int(s1)
except Exception as e:
    print(e)

為什麽不直接用萬能異常?分兩種情況去看:

1.如果你想要的效果是,無論出現什麽異常,我們統一丟棄,或者使用同一段代碼邏輯去處理他們,那麽騷年,大膽的去做吧,只有一個Exception就足夠了。

s1 = hello
try:
    int(s1)
except Exception,e:
    丟棄或者執行其他邏輯
    print(e)

#如果你統一用Exception,沒錯,是可以捕捉所有異常,但意味著你在處理所有異常時都使用同一個邏輯去處理(這裏說的邏輯即當前expect下面跟的代碼塊)

Exception

2.如果你想要的效果是,對於不同的異常我們需要定制不同的處理邏輯,那就需要用到多分支了。

技術分享圖片
s1 = hello
try:
    int(s1)
except IndexError as e:
    print(e)
except KeyError as e:
    print(e)
except ValueError as e:
    print(e)

 多分支
多分支 技術分享圖片多分支+Exception

part5:異常的其他機構

s1 = hello
try:
    int(s1)
except IndexError as e:
    print(e)
except KeyError as e:
    print(e)
except ValueError as e:
    print(e)
#except Exception as e:
#    print(e)
else:
    print(try內代碼塊沒有異常則執行我)
finally:
    print(無論異常與否,都會執行該模塊,通常是進行清理工作)

part6:主動觸發異常

try:
    raise TypeError(‘類型錯誤‘)
except Exception as e:
    print(e)

part7:自定義異常

技術分享圖片
class EvaException(BaseException):
    def __init__(self,msg):
        self.msg=msg
    def __str__(self):
        return self.msg

try:
    raise EvaException(‘類型錯誤‘)
except EvaException as e:
    print(e)

part8:try..except的方式比較if的方式的好處

try..except這種異常處理機制就是取代if那種方式,讓你的程序在不犧牲可讀性的前提下增強健壯性和容錯性

異常處理中為每一個異常定制了異常類型(python中統一了類與類型,類型即類),對於同一種異常,一個except就可以捕捉到,可以同時處理多段代碼的異常(無需‘寫多個if判斷式’)減少了代碼,增強了可讀性

使用try..except的方式

1:把錯誤處理和真正的工作分開來
2:代碼更易組織,更清晰,復雜的工作任務更容易實現;
3:毫無疑問,更安全了,不至於由於一些小的疏忽而使程序意外崩潰了;

python:包與異常處理