Python开发【第七篇】: 面向对象和模块补充
內(nèi)容概要
?
1. 特殊成員
什么是特殊成員呢? __init_()就是個特殊的成員. 帶雙下劃線的都是特殊方法. 這些方法在特殊的場景的時候會被自動的執(zhí)行. 比如
1. 類名() 會自動執(zhí)行__init__()
2. 對象() 會自動執(zhí)行__call__()
3. 對象[key] 會自動執(zhí)行__getitem__()
4. 對象[key] = value 會自動執(zhí)行__setitem__()
5. del 對象[key] 會自動執(zhí)行 __delitem__()
6. 對象+對象 會自動執(zhí)行 __add__()
7. with 對象 as 變量 會自動執(zhí)行__enter__ 和__exit__
8. 打印對象的時候 會自動執(zhí)行 __str__
9. 干掉可哈希 __hash__ == None 對象就不可哈希了.
?
?
創(chuàng)建對象的真正步驟:
首先, 在執(zhí)行類名()的時候. 系統(tǒng)會自動先執(zhí)行__new__()來開辟內(nèi)存. 此時新開辟出來的內(nèi)存區(qū)域是空的. 緊隨其后, 系統(tǒng)自動調(diào)用__init__()來完成對象的初始化?作. 按照時間軸來算.
1. 加載類
2. 開辟內(nèi)存(__new__)
3. 初始化(__init__)
4. 使用對象干xxx
類似的操作還有很多很多. 我們不需要完全刻意的去把所有的特殊成員全都記住. 實(shí)戰(zhàn)中也用不到那么多. 用到了查就是了.
?
單例模式:
class Foo(object):_instance = None # 實(shí)例# 先def __new__(cls, *args, **kwargs):if Foo._instance == None:Foo._instance = object.__new__(cls) # 開辟內(nèi)存return Foo._instance# 后def __init__(self):print("我是一個簡單的__init__")f1 = Foo() # 第一步先執(zhí)行__new__分配內(nèi)存, 第二步執(zhí)行__init__初始化這段內(nèi)存 f2 = Foo() print(f1) print(f2) View Code?
2. 反射
python面向?qū)ο笾械姆瓷?#xff1a;通過字符串的形式操作對象相關(guān)的屬性。python中的一切事物都是對象(都可以使用反射)
關(guān)于反射, 一共有4個函數(shù):
1. hasattr(obj, str) 判斷obj中是否包含str成員
2. getattr(obj,str) ?? 從obj中獲取str成員
3. setattr(obj, str, value) ? 把obj中的str成員設(shè)置成value. 注意. 這里的value可以是值, 也可以是函數(shù)或者方法
4. delattr(obj, str) ? 把obj中的str成員刪除掉
注意, 以上操作都是在內(nèi)存中進(jìn)行的. 并不會影響源代碼
?
四個方法的使用示例:
class Foo:f = "類的靜態(tài)變量"def __init__(self,name,age):self.name = nameself.age = agedef func1(self):print("hi, %s" %self.name)obj = Foo("zhouxingxing",18)# 拿著功能的名字,去對象或者模塊里找對應(yīng)的功能 print(hasattr(obj,'name')) # True 存在返回True print(hasattr(obj,'func1')) # True print(hasattr(obj,'gender')) # False 不存在返回False# 獲取屬性 # getattr(object, name, default=None) n = getattr(obj,"name") print(n) # lishichaofunc = getattr(obj,"func1") func() # hi, lishichao# print(getattr(obj,"aaaa")) # AttributeError: 'Foo' object has no attribute 'aaaa' 報錯# 第三個參數(shù):默認(rèn)值,如果該屬性不存在,則使用默認(rèn)值 print(getattr(obj,"aaaa","不存在啊")) # 不存在啊# 設(shè)置屬性 # setattr(object,name,value) setattr(Foo,"f1","類的靜態(tài)變量") # 設(shè)置類變量 setattr(obj,"sb",True) # 設(shè)置實(shí)例變量 setattr(obj,"show_name",lambda self:self.name+"sb") # 設(shè)置屬性 print(obj.__dict__) # {'name': 'lishichao', 'age': 18, 'sb': True, 'show_name': <function <lambda> at 0x00000000021E1730>} print(obj.show_name(obj)) # zhouxingxingsb# 刪除屬性 delattr(obj,"age") delattr(obj,"show_name") # delattr(obj,"name111") #不存在,則報錯。 AttributeError: name111 print(obj.__dict__) # {'name': 'zhouxingxing', 'sb': True} 在內(nèi)存中都被刪掉了 View Code?
反射當(dāng)前模塊成員:
import sysdef s1():print('s1')def s2():print('s2')this_mod = sys.modules[__name__] # <module '__main__' from 'E:/python-25期課上代碼/day07/課上代碼/02 反射.py'> print(hasattr(this_mod,"s1")) # True func = getattr(this_mod,"s2") func() # s2 View Code導(dǎo)入其他模塊,利用反射查找該模塊是否存在某個方法
#!/usr/bin/env python3 # _*_ coding:utf-8 _*_def t1():print('from the t1')def t2():print('from the t2')def t3():print('from the t3')money = "25000" master import master while 1:# 根據(jù)用戶輸入的功能進(jìn)行調(diào)用tool = input("請輸入你要執(zhí)行的功能的名字:") # chi# 拿著功能的名字,去對象或者模塊里找對應(yīng)的功能# has 有 attr 屬性if hasattr(master, tool):# 把這個功能拿出來fn = getattr(master, tool)if callable(fn): # 判斷是否可以被調(diào)用fn() # 調(diào)用函數(shù)else:print(fn) # 打印變量else:print("沒有這個功能") 導(dǎo)入master?
# 對象中的反射,類本身也是對象(python 一切皆對象)
class Foo(object):staticField = "old boy"def __init__(self):self.name = 'wupeiqi'def func(self):return 'func'@staticmethoddef bar():return 'bar'obj = Foo() print(getattr(Foo, 'staticField')) func1 = getattr(Foo, 'func') print(func1(obj)) func2 = getattr(Foo, 'bar') print(func2()) View Code?
?
3. configparser模塊
該模塊適用于配置文件件的格式與windows ini文件類似,可以包含一個或多個節(jié)(section)每個節(jié)可以有多個參數(shù)(鍵=值). 首先, 我們先看一個xxx服務(wù)器的配置文件
[DEFAULT] ServerAliveInterval = 45 Compression = yes CompressionLevel = 9 ForwardX11 = yes[server] User = hg Bind = 0.0.0.0 Port = 8888[client] IP = 127.0.0.0 Port = 50022 ForwardX11 = no?
我們用configparser就可以對這樣的文件進(jìn)行處理.首先, 是初始化
# 1. 初始化 import configparser config = configparser.ConfigParser()config["DEFAULT"] = {"sleep": 1000,"session-time-out": 30,"user-alive": 999999 }config["TEST-DB"] = {"db_ip": "192.168.17.189","port": "3306","u_name": "root","u_pwd": "123456" }config['168-DB'] = {"db_ip": "152.163.18.168","port": "3306","u_name": "root","u_pwd": "123456" }f = open("config.ini",mode="w") config.write(f) # 寫入文件 f.flush() f.close()# 讀取文件信息: config = configparser.ConfigParser()config.read("config.ini") # 讀取文件 print(config.sections()) # 獲取到所有section(章節(jié))。 [DEFAULT]是默認(rèn)信息,每個章節(jié)都有[DEFAULT]中的信息 # 執(zhí)行結(jié)果: ['TEST-DB', '168-DB']# 從章節(jié)中獲取到任意的數(shù)據(jù),get 方法Section下的key對應(yīng)的value print(config.get("TEST-DB","db_ip")) # 執(zhí)行結(jié)果: 192.168.17.189# 也可以像字典一樣操作 print(config["TEST-DB"]['db_ip']) print(config["168-DB"]["db_ip"]) # 執(zhí)行結(jié)果: # 192.168.17.189 # 152.163.18.168# 循環(huán)TEST-DB章節(jié)下的所有key for k in config["TEST-DB"]:print(k)# 所有key:value for k,v in config["TEST-DB"].items():print(k,v)print(config.options("TEST-DB")) # 同for循環(huán),找到 "TEST-DB"下所有鍵 # ['db_ip', 'port', 'u_name', 'u_pwd', 'sleep', 'session-time-out', 'user-alive']print(config.items("TEST-DB")) # 找到 "TEST-DB"下所有鍵值對 # [('sleep', '1000'), ('session-time-out', '30'), ('user-alive', '999999'), ('db_ip', '192.168.17.189'), ('port', '3306'), ('u_name', 'root'), ('u_pwd', '123456')] View Code?
增刪改操作:
# 先讀取. 然后修改. 最后寫回文件 config = configparser.ConfigParser() config.read("config.ini") # 讀取文件# 添加一個章節(jié) config.add_section("172-DB") config["172-DB"] = {"db_ip": "172.168.12.11","port": "3306","u_name": "root","u_pwd": "123456" }# 修改信息 config.set("168-DB","db_ip","10.0.3.26")# 刪除章節(jié) config.remove_section("TEST-DB")# 刪除元素信息 config.remove_option("168-DB","db_ip")# 寫回文件 config.write(open("config.ini",mode="w")) View Code?
?
4. MD5加密
MD5是一種不可逆的加密算法. 它是可靠的. 并且安全的. 在python中我們不需要手寫這一套算法. 只需要引入hashlib模塊就能搞定MD5的加密工作
import hashlib# 1. 創(chuàng)建對象 obj = hashlib.md5() # 2. 把要加密的內(nèi)容寫入對象 obj.update("lishichao".encode("utf-8")) # 加密的必須是字節(jié) # 3. 獲取到MD5 miwen = obj.hexdigest() print(miwen) # 5f71293582408cc955d1a41fc434d29a
?
那這樣的密文安全么? 其實(shí)是不安全的. 當(dāng)我們用這樣的密文去找一個所謂的MD5解密工具. 是有可能解密成功的.?
?
MD5不是不可逆么? 注意. 這里有一個叫撞庫的問題. 就是. 由于MD5的原始算法已經(jīng)存在很久了. 那就有一些人用一些簡單的排列組合來計算MD5. 然后當(dāng)出現(xiàn)相同的MD5密文的時候就很容易反推出原來的數(shù)據(jù)是什么. 所以并不是MD5可逆,而是有些別有用心的人把MD5的常見數(shù)據(jù)已經(jīng)算完并保留起來了.
那如何應(yīng)對呢? 加鹽就行了. 在使用MD5的時候. 給函數(shù)的參數(shù)傳遞一個byte即可.
?
md5的應(yīng)用:
def my_md5(s):obj = hashlib.md5(b"asjdkanoiwhdwbiohjixvzx")obj.update(s.encode("utf-8"))miwen = obj.hexdigest()return miwenusername = "" password = ""# 用戶登錄 def login():uname = input("請輸入用戶名:")upwd = input("請輸入密碼:")if uname == username and my_md5(upwd) == password:print("登錄成功")else:print("登錄失敗")# 用戶注冊 def reg():global usernameglobal passworduname = input("請輸入用戶名:")upwd = input("請輸入密碼:")username = unamepassword = my_md5(upwd) # 密碼放密文reg() login() View Code?
文件的MD5,用來校驗(yàn)文件是否傳輸完整:
obj = hashlib.md5(b"asjdkanoiwhdwbiohjixvzx")# 文件獲取MD5值 f = open("test.txt",mode="r",encoding="utf-8") for i in f:obj.update(i.encode("utf-8"))miwen = obj.hexdigest() print(miwen) # 5fcda51b94a2e76d0322881d457c5c4f# 修改文件內(nèi)容后MD5值會有變化: # be6e5639222ad8a950eaa98e7e66dc73 View Code?
5. 日志
論日志的重要性,統(tǒng)計數(shù)據(jù),報錯信息,程序運(yùn)行記錄都由日志記錄。在python中創(chuàng)建日志系統(tǒng):
1. 導(dǎo)入logging模塊.
2. 簡單配置logging
3. 出現(xiàn)異常的時候(except). 向日志中寫錯誤信息.
?
# 對日志系統(tǒng)進(jìn)行配置
# 單日志系統(tǒng)
?
?# 多文件日志系統(tǒng)
# 創(chuàng)建一個操作日志的對象logger(依賴FileHandler) file_handler1 = logging.FileHandler('l1.log', 'a', encoding='utf-8') file_handler1.setFormatter(logging.Formatter(fmt="%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s")) logger1 = logging.Logger('汽車融資租賃', level=logging.ERROR) logger1.addHandler(file_handler1)file_handler2 = logging.FileHandler('l2.log', 'a', encoding='utf-8') file_handler2.setFormatter(logging.Formatter(fmt="%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s")) logger2 = logging.Logger('IHOS醫(yī)療衛(wèi)生綜合服務(wù)系統(tǒng)', level=logging.ERROR) logger2.addHandler(file_handler2)logger1.error("我出錯了. 我的車找不到了") logger2.error("醫(yī)院丟了") View Code?
即輸出到屏幕,又寫入到文件
import logging # 創(chuàng)建 logging 對象 logger = logging.getLogger()# 創(chuàng)建文件對象 fh1 = logging.FileHandler("a1.log",encoding="utf-8")# 創(chuàng)建屏幕對象 sh = logging.StreamHandler()# 日志格式 formater1 = logging.Formatter(fmt='%(asctime)s %(filename)s [line:%(lineno)d] %(levelname)s %(message)s', # 顯示格式datefmt='%Y-%m-%d %H:%M:%S',)formater2 = logging.Formatter(fmt='%(asctime)s %(filename)s [line:%(lineno)d] %(levelname)s %(message)s', # 顯示格式datefmt='%Y-%m-%d %H:%M:%S',)# 給對象綁定格式 fh1.setFormatter(formater1) sh.setFormatter(formater2)# 給logger對象添加其他對象 logger.addHandler(fh1) logger.addHandler(sh)# 設(shè)置logger級別 logger.setLevel(10)fh1.setLevel(40) sh.setLevel(50)logging.debug('調(diào)試模式') # 10 logging.info('正常運(yùn)行') # 20 logging.warning('警告') # 30 logging.error('錯誤') # 40 logging.critical('系統(tǒng)崩了') # 50 View Code?
6. 異常處理?
什么是異常? 異常是程序在運(yùn)行過程中產(chǎn)生的錯誤. 我們先制造一個錯誤. 來看看異常長什么樣.
def chu(a, b):return a/b ret = chu(10, 0) print(ret)# 執(zhí)行結(jié)果: Traceback (most recent call last):File "E:/python-25期課上代碼/day07/課上代碼/06 異常處理.py", line 5, in <module>ret = chu(10, 0)File "E:/python-25期課上代碼/day07/課上代碼/06 異常處理.py", line 4, in chureturn a/b ZeroDivisionError: division by zero View Code?
什么錯誤呢. 除法中除數(shù)不能是0. 那如果真的出了這個錯. 你把這一堆信息拋給客戶么? 肯定不能. 那如何處理呢??
def chu(a, b):return a/btry:ret = chu(10, 0)print(ret) except Exception as e:print("除數(shù)不能是0")# 執(zhí)行結(jié)果: 除數(shù)不能是0 View Code try ... except 是嘗試著運(yùn)行xxx代碼. 出現(xiàn)了錯誤. 就執(zhí)行except后面的代碼. 在這個過程中. 當(dāng)代碼出現(xiàn)錯誤的時候. 系統(tǒng)會產(chǎn)生一個異常對象. 然后這個異常會向外拋. 被except攔截. 并把接收到的異常對象賦值給e. e就是異常對象.
Exception 是所有異常的基類, 也就是異常的根. 換句話說. 所有的錯誤都是Exception的子類對象. 我們看到的ZeroDivisionError 其實(shí)就是Exception的子類.
Exception 表示所有的錯誤. 太籠統(tǒng)了. 所有的錯誤都會被認(rèn)為是Exception.當(dāng)程序中出現(xiàn)多種錯誤的時候, 就不好分類了, 最好是出什么異常就用什么來處理.在try...execpt語句中.
還可以寫更多的except:
?
try:print("各種操作....") except ZeroDivisionError as e:print("除數(shù)不能是0") except FileNotFoundError as e:print("?件不存在") except Exception as e:print("其他錯誤")此時. 程序運(yùn)行過程中. 如果出現(xiàn)了ZeroDivisionError就會被第一個except捕獲. 如果出現(xiàn)了FileNotFountError就會被第二個except捕獲. 如果都不是這兩個異常. 那就會被最后的Exception捕獲. 總之最后的Exception就是我們異常處理的最后一個守門員. 這時我們最常用的一套寫法.
接下來. 給出一個完整的異常處理寫法(語法):
try: '''操作''' except Exception as e:'''異常的父類,可以捕獲所有的異常''' else:'''保護(hù)不拋出異常的代碼, 當(dāng)try中無異常的時候執(zhí)行''' finally:'''最后總是要執(zhí)行我'''解讀: 程序先執(zhí)行操作, 然后如果出錯了會走except中的代碼. 如果不出錯, 執(zhí)行else中的代碼. 不論處不出錯. 最后都要執(zhí)行finally中的語句. 一般我們用try...except就夠用了. 頂多加上finally. finally一般用來作為收尾工作.?
示例:
def chu(a, b):return a/btry:chu(10, 0) except Exception as e:print("其他錯誤") else:print("沒有異常,正常執(zhí)行") finally:print("不管有沒有異常,最后要執(zhí)行我") # 執(zhí)行結(jié)果: # 其他錯誤 # 不管有沒有異常,最后要執(zhí)行我try:chu(10, 2) except Exception as e:print("其他錯誤") else:print("沒有異常,正常執(zhí)行") finally:print("不管有沒有異常,最后要執(zhí)行我") # 執(zhí)行結(jié)果: # 沒有異常,正常執(zhí)行 # 不管有沒有異常,最后要執(zhí)行我 View Code?
上面是處理異常. 我們在執(zhí)行代碼的過程中如果出現(xiàn)了一些條件上的不對等. 根本不符合我的代碼邏輯. 比如. 參數(shù). 我要求你傳遞一個數(shù)字. 你非得傳遞一個字符串. 那對不起. 我沒辦法幫你處理. 那如何通知你呢? 兩個方案.
方案一. 直接返回即可.
方案二. 拋出一個異常.
第一種不夠好,無法起到警示作用,所以直接拋一個錯誤出去. 那怎么拋呢? 我們要用到raise關(guān)鍵字
當(dāng)程序運(yùn)行到raise. 程序會被中斷. 并實(shí)例化后面的異常對象. 拋給調(diào)用方. 如果調(diào)用方不處理. 則會把錯誤繼續(xù)向上拋出. 最終拋給用戶. 如果調(diào)用方處理了異常. 那程序可以正常的執(zhí)行.
?
自定義異常:
自己寫的代碼中出現(xiàn)了一個無法用現(xiàn)有的異常來解決問題的時候,需要自定義異常
自定義異常: 寫的類繼承了Exception類. 那這個類就是一個異常類.
?
class GenderError(Exception):passclass Person:def __init__(self, name, gender):self.name = nameself.gender = genderdef Man(p): # 女if p.gender != "男":raise GenderError("進(jìn)錯了,這里是男澡堂") # 拋出異常else:print("歡迎光臨")p1 = Person("alex", "男") p2 = Person("景女神", "女")Man(p2) # 報錯,程序就停了 Man(p1)# 處理異常 try:Man(p2)Man(p1) except GenderError as e:print(e) # e => 進(jìn)錯了,這里是男澡堂? except Exception as e:print("反正報錯了") 示例?
我們在調(diào)試的時候, 最好是能看到錯誤源自于哪里,需要引入另一個模塊traceback. 這個模塊可以獲取到我們每個方法的調(diào)用信息.又被成為堆棧信息. 這個信息用來排錯是很有幫助的.
import traceback# 繼承 Exception 就是異常類 class GenderError(Exception):passclass Person:def __init__(self,name,gender):self.name = nameself.gender = genderdef Man(person):if person.gender != "男":raise GenderError("性別不對")p1 = Person("周星星","男") p2 = Person("張敏","女")# Man(p1) # Man(p2) # 報錯 會拋出異常: GenderError# 處理異常 try:Man(p1)Man(p2) except GenderError as e:val = traceback.format_exc() # 獲取到堆棧信息print(e)print(val) except Exception as e:print("反正報錯了") View Code?
執(zhí)行結(jié)果:
當(dāng)測試代碼的時候把堆棧信息打印出來. 但是當(dāng)?shù)搅司€上的生產(chǎn)環(huán)境的時候把這個堆棧去掉即可.?
?
異常信息記錄日志:
import logging import tracebacklogging.basicConfig(filename='error.log', # 日志文件名# %(asctime) 時間# %(name) root# %(levelname)s 事件的嚴(yán)重性# %(module)s 不用管# %(message)format='%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s',datefmt='%Y-%m-%d %H:%M:%S', # 時間格式level=30) # 記錄日志的最低級別try:print(1/0) except Exception:logging.error(traceback.format_exc()) View Code?
?
日志內(nèi)容:
?
?
7. 模塊
01.? 模塊
什么是模塊. 模塊就是一個包含了python定義和聲明的文件, 文件名就是模塊的名字加上.py后綴。我們寫的py文件都可以看成是一個模塊但是我們import加載的模塊一共分成四個通用類別:
1. 使用pyhton編寫的py文件
2. 已被變異為共享庫或者DLL或C或者C++的擴(kuò)展
3. 包好組模塊的包.
4. 使用c編寫并連接到python解釋器的內(nèi)置模塊
為什么要使用模塊? 為了我們寫的代碼可以重用. 不至于把所有的代碼都寫在一個文件內(nèi). 當(dāng)項(xiàng)目規(guī)模比較大的時候. 就必須要把相關(guān)的功能進(jìn)行分離. 方便維護(hù)和開發(fā)。
如何使用模塊?導(dǎo)入模塊有兩種方式
1. import 模塊
2. from xxx import xxx
02. import
?示例文件:自定義模塊my_module.py,文件名my_module.py,模塊名my_module
# 自定義模快 my_module.py # 示例文件:文件名my_module.py,模塊名my_module print('from the my_module.py')money=1000def read1():print('my_module->read1->money',money)def read2():print('my_module->read2 calling read1')read1()def change():global moneymoney=0 my_module?
引用 my_module 模塊
# 導(dǎo)入模塊 import my_module print(my_module.money) # 使用模塊中定義好的變量my_module.read1() # 調(diào)用模塊中的函數(shù) View Code?
在Python中模塊是不能夠重復(fù)導(dǎo)入的. 當(dāng)重復(fù)導(dǎo)入模塊時. 系統(tǒng)會根據(jù) sys.modules 來判斷該模塊是否已經(jīng)導(dǎo)入了. 如果已經(jīng)導(dǎo)入. 則不會重復(fù)導(dǎo)入
import sys print(sys.modules.keys()) # 查看導(dǎo)入的模塊. import my_module # 導(dǎo)入模塊. 此時會默認(rèn)執(zhí)行該模塊中的代碼 import my_module # 該模塊已經(jīng)導(dǎo)入過了. 不會重復(fù)執(zhí)行代碼 import my_module import my_module import my_module import my_module# 執(zhí)行結(jié)果: # dict_keys(['builtins', 'sys', '_frozen_importlib', '_imp', '_warnings', '_thread', '_weakref', '_frozen_importlib_external', '_io', 'marshal', 'nt', 'winreg', 'zipimport', 'encodings', 'codecs', '_codecs', 'encodings.aliases', 'encodings.utf_8', '_signal', '__main__', 'encodings.latin_1', 'io', 'abc', '_weakrefset', 'site', 'os', 'errno', 'stat', '_stat', 'ntpath', 'genericpath', 'os.path', '_collections_abc', '_sitebuiltins', 'sysconfig', 'sitecustomize']) # from the my_module.py View Code?
導(dǎo)入模塊的時候都做了些什么? 首先. 在導(dǎo)入模塊的一瞬間. python解釋器會先通過sys.modules來判斷該模塊是否已經(jīng)導(dǎo)入了該模塊. 如果已經(jīng)導(dǎo)入了則不再導(dǎo)入. 如果該模塊還未導(dǎo)入過. 則系統(tǒng)會做三件事.
1. 為導(dǎo)入的模塊創(chuàng)立新的名稱空間
2. 在新創(chuàng)建的名稱空間中運(yùn)行該模塊中的代碼
3. 創(chuàng)建模塊的名字. 并使用該名稱作為該模塊在當(dāng)前模塊中引用的名字.
我們可以使用globals來查看模塊的名稱空間
print(globals()) {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000000001DBB518>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'E:/python-25期課上代碼/day07/課上代碼/07 模塊.py', '__cached__': None, 'sys': <module 'sys' (built-in)>, 'my_module': <module 'my_module' from 'E:\\python-25期課上代碼\\day07\\課上代碼\\my_module.py'>}?
由于模塊在導(dǎo)入的時候會創(chuàng)建其自己的名稱空間. 所以. 我們在使用模塊中的變量的時候一般是不會產(chǎn)生沖突的
import my_modlue money = 2000 print(my_module.money) # 模塊中的變量 print(money) #自己的變量 # 執(zhí)行結(jié)果: # 1000 # 2000 View Code?
為模塊名起別名,相當(dāng)于m1=1;m2=m1?
import my_module as sm print(sm.money)?
在一行導(dǎo)入多個模塊
import sys,os,re?
?
模塊搜索路徑:
python解釋器在啟動時會自動加載一些模塊,可以使用sys.modules查看
在第一次導(dǎo)入某個模塊時(比如my_module),會先檢查該模塊是否已經(jīng)被加載到內(nèi)存中(當(dāng)前執(zhí)行文件的名稱空間對應(yīng)的內(nèi)存),如果有則直接引用
如果沒有,解釋器則會查找同名的內(nèi)建模塊,如果還沒有找到就從sys.path給出的目錄列表中依次尋找my_module.py文件。
所以總結(jié)模塊的查找順序是:內(nèi)存中已經(jīng)加載的模塊->內(nèi)置模塊->sys.path路徑中包含的模塊
# windows: python解釋器模塊搜索路徑是項(xiàng)目根目錄,和當(dāng)前目錄,通過 sys.path 查看模塊搜索路徑 # linux: 只有當(dāng)前的目錄,需要 sys.path.append() 把項(xiàng)目根目錄添加進(jìn)去?
main是什么,main是程序的入口
我們可以通過模塊的全局變量__name__來查看模塊名: 當(dāng)做腳本運(yùn)行: __name__ 等于'__main__'當(dāng)做模塊導(dǎo)入: __name__= 模塊名def main():passif __name__ == "__main__": # 啟動文件, 被當(dāng)做模塊導(dǎo)入時 不執(zhí)行。main()
?
正確的導(dǎo)入模塊的順序:
1. 所有的模塊導(dǎo)入都要寫在最上?. 這是最基本的
2. 先引入內(nèi)置模塊
3. 再引入擴(kuò)展模塊
4. 最后引入你自己定義的模塊
?
03. from xxx import? xxx
在使用from的時候, python也會給我們的模塊創(chuàng)建名稱空間. 這一點(diǎn)和import是一樣的. 但是from xxx import xxx的時候. 我們是把這個空間中的一些變量引入過來了. 說白了. 就是部分導(dǎo)入. 當(dāng)這個模塊中的內(nèi)容過多的時候. 可以選擇性的導(dǎo)入要使用的內(nèi)容
from my_module import read1 read1()此時是可以正常運(yùn)行的. 但是我們省略了之前的模塊.函數(shù)() 直接函數(shù)()就可以執(zhí)行了, 并且from語句也支持一行語句導(dǎo)入多個內(nèi)容.
from my_module import read1,read2,change read1() read2() change()?
同樣支持as
from my_module import read1 as rd rd()?
from? xxx? import? *?
是把模塊中的所有內(nèi)容都導(dǎo)入. 注意, 如果模塊中沒有寫出__all__ 則默認(rèn)所有內(nèi)容都導(dǎo)入. 如果寫了__all__ 此時導(dǎo)入的內(nèi)容就是在__all__列表中列出來的所有名字.
# haha.py __all__ = ["money", "chi"] money = 100def chi(): print("我是吃") def he(): print("我是呵呵")# test.py from haha import * chi() print(money) # he() # 報錯 View Code?
最后. 看一下from的坑. 當(dāng)我們從一個模塊中引入一個變量的時候. 如果當(dāng)前文件中出現(xiàn)了重名的變量時. 會覆蓋掉模塊引入的那個變量.
from my_module import money money = 10 print(money) # 10?
所以. 不要重名. 切記. 不要重名! 不僅僅是變量名不要重復(fù). 我們自己創(chuàng)建的py文件的名字不要和系統(tǒng)內(nèi)置的模塊重名. 否則. 引入的模塊都是python內(nèi)置的模塊.
?
?
?
8. 包
什么是包?
包是一種通過 '.模塊名' 來組織python模塊名稱空間的方式. 那什么樣的東西是包呢? 我們創(chuàng)建的每個文件夾都可以被稱之為包. 但是我們要注意, 在python2中規(guī)定. 包內(nèi)必須存在__init__.py文件. 創(chuàng)建包的目的不是為了運(yùn)行, 而是被導(dǎo)入使用. 包只是一種形式而已. 包的本質(zhì)就是一種模塊
為何要使包?
包的本質(zhì)就是一個文件夾, 那么文件夾唯一的功能就是將文件組織起來,隨著功能越寫越多, 我們無法將所有功能都放在一個文件中, 于是我們使用模塊去組織功能,隨著模塊越來越多, 我們就需要用文件夾將模塊文件組織起來, 以此來提高程序的結(jié)構(gòu)性和可維護(hù)性
?
首先, 我們先創(chuàng)建一些包. 用來作為接下來的學(xué)習(xí). 包很好創(chuàng)建. 只要是一個文件夾, 有__init__.py就可以
?創(chuàng)建目錄結(jié)構(gòu):
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/__init__.py','w')) l.append(open('glance/db/models.py','w')) map(lambda f:f.close() ,l) View Code?
#文件內(nèi)容 #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) 文件內(nèi)容?
?
?包的導(dǎo)入:
1.關(guān)于包相關(guān)的導(dǎo)入語句也分為import和from ... import ...兩種,但是無論哪種,無論在什么位置,在導(dǎo)入時都必須遵循一個原則:凡是在導(dǎo)入時帶點(diǎn)的,點(diǎn)的左邊都必須是一個包,否則非法。可以帶有一連串的點(diǎn),如item.subitem.subsubitem,但都必須遵循這個原則。
2.對于導(dǎo)入后,在使用時就沒有這種限制了,點(diǎn)的左邊可以是包,模塊,函數(shù),類(它們都可以用點(diǎn)的方式調(diào)用自己的屬性)。
3.對比import item 和from item import name的應(yīng)用場景:
如果我們想直接使用name那必須使用后者。
?
?import ,在與包 glance 同級別的文件中測試,test.py文件。
import glance.db.models glance.db.models.register_models("mysql")?
from ... import ...
需要注意的是from后import導(dǎo)入的模塊,必須是明確的一個不能帶點(diǎn),否則會有語法錯誤,如:from a import b.c是錯誤語法
還是 test.py 與 glance 目錄同級
from glance.db import models models.register_models("mysql")from glance.db.models import register_models register_models("redis")?
__init__ 文件
不管是哪種方式,只要是第一次導(dǎo)入包或者是包的任何其他部分,都會依次執(zhí)行包下的__init__.py文件(我們可以在每個包的文件內(nèi)都打印一行內(nèi)容來驗(yàn)證一下),這個文件可以為空,但是也可以存放一些初始化包的代碼。
?
?
from? glance.api? import? *
?
?
在講模塊時,我們已經(jīng)討論過了從一個模塊內(nèi)導(dǎo)入所有*,此處我們研究從一個包導(dǎo)入所有*。
?
此處是想從包api中導(dǎo)入所有,實(shí)際上該語句只會導(dǎo)入包api下 __init__. py文件中定義的名字,我們可以在這個文件中定義__all___:
?
?
#在__init__.py中定義 print("我是api包下的__init__.py文件") x=10def func():print('from api.__init.py')__all__=['x','func','policy'] api目錄下的__init.py?
?
此時我們在于 glance 同級的 test.py 文件中執(zhí)行 from glance.api import * 就導(dǎo)入__all__中的內(nèi)容(versions仍然不能導(dǎo)入)。
?
?
from glance.api import * policy.get() print(x) func() versions.create_resource("config.ini") # 報錯,沒有導(dǎo)入# 我是api包下的__init__.py文件 # from policy.py # 10 # from api.__init.py test.py?
?
?
絕對導(dǎo)入和相對導(dǎo)入
?
我們的最頂級包glance是寫給別人用的,然后在glance包內(nèi)部也會有彼此之間互相導(dǎo)入的需求,這時候就有絕對導(dǎo)入和相對導(dǎo)入兩種方式:
?
絕對導(dǎo)入:以glance作為起始
?
相對導(dǎo)入:用. 或者.. 的方式做為起始(只能在一個包中使用,不能用于不同目錄內(nèi))
?
例如:我們在 glance/api/version.py 中想要導(dǎo)入 glance/cmd/manage.py
# 絕對導(dǎo)入 import sys # from glance.cmd import manage# 相對導(dǎo)入 # ValueError: attempted relative import beyond top-level package # versions不能作為啟動文件, 啟動文件要與glance在同級目錄 from ..cmd import managedef create_resource(conf):manage.main()print('from version.py: ',conf) glance/api/version.py?
測試結(jié)果:在于glance同級的 test.py 文件中測試
?
?
# 啟動文件 from glance.api import versionsif __name__ == '__main__':versions.create_resource("config") 啟動文件?
轉(zhuǎn)載于:https://www.cnblogs.com/root0/p/10430805.html
總結(jié)
以上是生活随笔為你收集整理的Python开发【第七篇】: 面向对象和模块补充的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 对我影响最大的三个老师
- 下一篇: java_codereview随笔