模塊
模塊是非常簡單的Python文件,單個Python文件就是一個模塊,兩個文件就是兩個模塊。
import語句是用來導入模塊或者從模塊里導入特定的類或者函數。如前面我們用過的math模塊,從而可以使用sqrt函數來計算距離。
假如有一個包含Database類的database.py的模塊。現有另一個模塊為product.py,它需要從database.py里實例化一個Database類,然后就可以在數據庫中執行相關產品查詢。
import database db = database.Database() #數據庫搜索操作
這時任何在database這個模塊里面的類或者函數,都可以通過database.<something>這種激發訪問。或者,也可以用from ... import語法來導入一個類。
from database import Database db = Database()
也可以將導入的類進行重命名
from database import Database as DB # 重命名 db = DB()
也可以在一行里面導入多項,比如database還含有Query類
from database import Database, Query
但是,很多教程與經驗建議不要導入模塊里面所有的類,如下的寫法是不采用的。
from database import *
組織模塊
一個包(package)就是放在一個文件夾里的模塊集合。包的名字就是文件夾的名字。我們需要做的是告訴python這個文件夾是一個包,並且把一個名為__init__.py的文件(通常是空的)放在這個文件夾里。如果我們忘記創建這個文件夾,就沒法從這個文件夾里面導入那些模塊。
例如在我們的工作目錄里,把我們的模塊放在了一個叫ecommerce(電子商務)的包里,這個目錄同樣包含一個main.py的文件用來啟動程序。在ecommerce包里再添加一個payments的包用來管理不同的付款方式,文件夾的層次結構如下所示:
parent_directory/ main.py ecommerce/ __init__.py database.py products.py payments/ __init__.py paypal.py authorizenet.py
其中producs.py的有Product類
class Product: pass
database.py有Database類
class Database: pass
模塊的導入方式有兩種:絕對導入和相對導入。
絕對導入
要先給出這個模塊、函數的完整路徑,如在main.py需要訪問produces模塊中的Product類,使用使用如下的方法進行絕對導入:
import ecommerce.products product = ecommerce.products.Product()
或者(個人比較喜歡這種方式):
from ecommerce.products import Product product = Product()
或者:
from ecommerce import products product = products.Product()
import語句使用點號作為分隔符來分隔包或者模塊。
上述的都可以使用,如果要導入一個模塊中的很多類,使用使用第三種方法,如果是指導入一個模塊的一兩個類,則可以使用第二種方法具體指明。
相對導入
在包(package)的情況下,如果知道父模塊的名稱,那么就可以使用相對導入。比如當前在products模塊下工作,想從隔壁的database模塊導入Database類,就可以使用相對導入:
from .database import Database # 點號表示使用當前路徑的database模塊
如果我們正在編輯ecommerce.payments包里的paypal模塊,需要引用父包里的database模塊:
from ..database import Database # 使用兩個點號表示訪問上層的父類
如果ecommerce有contact包,該包里有email模塊,需要將該模塊的sendEmail函數導入到paypal模塊中,
from ..contact.email import sendEmail
數據訪問權限
大部分的面向對象的編程語言都有一個“訪問控制”的概念,比如私有的(private)、受保護的(protected)和公共的(public)。但python並沒有這種強制規定。在技術層面上,一個類里的所有方法和屬性都是公共可訪問的,以下有三種形式建議不同的訪問形式:
1、使用注釋進行提示建議。如可以在docstring里面放一個提示來表明這個方法只是內部使用的
2、給某個屬性或者方法加一個下划線的前綴,大部分python程序員會把這個解釋為“這是個內部變量,使用之前要三思”
3、給某個屬性或者方法添加一個雙下划線的前綴,強烈建議為內部變量。訪問時需要名稱改編(name mangling),即在該方法或者屬性前面自動加一個_<classname>的前綴(單下划線)。
一般情況下,不會使用加下划線或者雙下划線的變量。
class SecretString: ''' A not-at-all secure way to store a secret string''' def __init__(self, plain_string, pass_phrase): self.__plain_string = plain_string self.__pass_phrase = pass_phrase def decrypt(self, pass_phrase): ''' Only show the string if the pass_phrase is correct.''' if pass_phrase == self.__pass_phrase: return self.__plain_string else: return
將上述代碼存儲為filename.py,然后使用python -i filename.py執行這個腳本,然后在交互的解釋器里進行如下的測試:
>>> secret_string = SecretString("ACME: Top Secret", "antwerp") >>> print(secret_string.decrypt("antwerp")) ACME: Top Secret >>> print(secret_string.__plain_text) Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'SecretString' object has no attribute '__plain_text' >>> print(secret_string._SecretString__plain_string) ACME: Top Secret
從包里直接導入變量
在ecommerce包里有兩個模塊,一個是database.py,另一個是products.py,假設database里面有一個db變量,這個變量在很多地方都會被訪問,那么我們下面的代碼將可以實現用import ecommerce.db取代import ecommerce.database.db。
通過在__init__.py文件(定義目錄為包),在這里文件中可以包含任意變量或者類的生命,而且它會作為這個包的一部分被我們使用。在這個例子中,如果有ecommerce/__init__.py文件里包含這么一行:
from .database import db
那么我們就可以用下面的語句,在mian.py或者其他文件訪問這個db屬性了:
from ecommerce import db
以上主要在於導致ecommerce.py這個文件是一個模塊而不是包的原因在於__init__.py。
如果你把所有代碼放在了一個單獨的模塊,之后又決定拆成一個包里的多個包,__init__.py文件同樣對你有幫忙。
其他模塊如果想要訪問這個新包,__init__.py文件仍然是主要的切入點。但是在內部,代碼仍然可以被組織成許多不同模塊或者子包。
參考:
1、《Python3 面向對象編程》 [加]Dusty Philips 著
