python模塊引用


前言

在軟件工程中,我們從大的宏觀方向,要看業務目標、工程架構,到具體實施時就要選擇適合工程實現的編程語言和配套組件。在選擇編程語言時根據項目的不同,我們可能會有很多需要考慮的因素,從編程語言本身的角度來看,他們是“大同小異”的,但如果從差異角度看,每種編程語言除了語法體現不同外,執行方式、性能、內存管理、模塊組織、組件、第三方庫等又會有很大差異,但這些卻又是我們要需要考慮的關鍵點。

下面我們聊一下python語言的模塊引入方式,當我們知道了其引入方式,那么組織模塊就是自己的事了~~

當然,本文中不一定會列出所有引入方式,我們只關注常用的、個人認為較好的方式。

在開始聊這個話題前,我們首先要區分的概念是 package、module、class,我們粗略總結:

1. package是一個層級的概念,它通常是一個目錄,在package中可能會包含多個module;

2. module是一個文件(通常是package中的文件),在文件中我們可以定義類或函數等;

3. class 類,它被包含在module文件中;

 

然后,對於引用格式,我們做一個“感性”的總結就是:package要用“from”,module和class要用import,如:

from package import module

import package.module    # 注意,不能這樣引用:import package.module.class

基本的模塊引入方式

為方便舉例說明,現假設有如下目錄結構及文件:

$ tree .
├── package1
│   ├── module1.py
│   └── module2.py
└── test.py

$ cat package1/module1.py 
class class1:
    def hello(self):
        print('I am class1 in module1')

$ cat package1/module2.py 
class class2:
    def hello(self):
        print('I am class2 in module2')

$ cat test.py 
from package1 import module1  # 方式1
from package1.module2 import class2  # 方式2  注意,不能這樣引入:import package1.module2.class2

module1.class1().hello()
class2().hello()

可以看到,test.py中展示了兩種基本的引入模塊和類的方式,並調用了hello方法。我們執行test.py,結果如下:

$ python3 test.py 
I am class1 in module1
I am class2 in module2

當然我們還可以用其他方式引入模塊,例如我們修改test.py文件為 :

import package1.module1
import package1.module2
from package1 import *  # 注意,前面一定要有兩個import語句,否則是不正確的

module1.class1().hello()
module2.class2().hello()

這種方式沒有語法錯誤,但個人不建議用這種方式,因為不太直觀。

進一步理解模塊的概念

細心的你可以已經注意到,上面的目錄結構中不能直接引入package1中的所有模塊(from package1 import * ),這是為什么呢?

其實原因很簡單:因為現在的package1還是一個普通的目錄,還算不上是一個真正意義上的package,需要在package下放一個__init__.py文件來標識當前目錄為一個package!

這里要說一下,我們在設計python程序的時候,只要是我們認為應該是一個package的目錄都應該包含一個__init__.py文件(即使該文件為空)。

下面我們在package1目錄下添加空的__init__.py文件,並修改test.py文件內容:

$ tree .
├── package1
│   ├── __init__.py
│   ├── module1.py
│   └── module2.py
└── test.py

$ cat test.py
from package1 import *
module1.class1().hello()
module2.class2().hello()

如果此時執行test.py會發現依然報錯!

這是因為,雖然此時package1是一個“package”了,但__init__.py文件內容為空(我們可以把__init__.py文件看做是一個package的“對外接口”,可以起到屏蔽“包”內細節的做用)。使用__init__.py文件一般需要注意以下兩點:

1. __init__.py文件內一般使用 __all__ 關鍵字來限定package內對外的模塊。

2. __init__.py文件盡量“輕”,即最好不要在里面定太多代碼(如定義類等),雖然這樣做不會報錯。

下面通過修改__init__.py文件和test.py文件來分別說明__init__.py文件的使用方式:

$ cat package1/__init__.py 
__all__ = ['module1', 'module2']

$ cat test.py 
from package1 import *

module1.class1().hello()
module2.class2().hello()

# 運行腳本
$ python3 test.py
I am class1 in module1
I am class2 in module2

當然,如果__init__.py文件內包含某個類,package外面也可以直接訪問到,如:

$ cat package1/__init__.py 
__all__ = ['module1', 'module2']

class class3:
    def hello(self):
        print('I am class3 in __init__.py')


$ cat test.py 
from package1 import module1, module2, class3

module1.class1().hello()
module2.class2().hello()
class3().hello()


$ python3 test.py 
I am class1 in module1
I am class2 in module2
I am class3 in __init__.py

注意,test.py 文件中指明了需要引入的內容 (from package1 import module1, module2, class3),如果將這個引入方式改為全部引入(from package1 import * )則會報錯,因為 import * 引入的package內容需要__init__.py文件中__all__關鍵字指定!

當然,如果要引入具體module的所有內容,可以通過 import * 的方式,如:from package1.module1 import *

更復雜的引用

我們可以想到,引用還有很多其他情況,如不同package間、不同目錄下或不同層級目錄下的相互引用等。

其實,所有的引用,在了解了以上原理后都是很好了解的,不過這里還要先提一下”環境變量“,關於環境變量概念和作用這里略過。python添加環境變量的方法:

sys.path.append('user_path')  # 其中 user_path 即我們要添加的路徑

添加到環境變量中的路徑,在本工程中各文件中都可以直接訪問到。下面我們利用和之前相似的例子進行說明。

假設我們現在有一個和package1同級的包,package2,且package2中有一個moudle3.py文件,此時package1中的module2文件需要訪問package2的module3.py,文件目錄及內容如下:

$ tree .
├── package1
│   ├── __init__.py
│   ├── module1.py
│   └── module2.py
├── package2
│   ├── __init__.py
│   └── module3.py
└── test.py

$ cat package1/__init__.py 
__all__ = ['module1', 'module2']

$ cat package1/module1.py 
class class1:
    def hello(self):
        print('I am class1 in module1')

$ cat package1/module2.py 
from package2.module3 import class3  # 這里我們引用了package2的class3
class class2:
    def hello(self):
        print('I am class2 in module2')

$ cat package2/__init__.py 
__all__ = ['module3']

$ cat package2/module3.py 
class class3:
    def hello(self):
        print('I am class3 in module3 which of package2')

$ cat test.py 
import sys, os
curr_dir = os.path.dirname(os.path.realpath(__file__))
sys.path.append(curr_dir)  # 這里我們添加了環境變量

from package1 import module1, module2

module1.class1().hello()
module2.class2().hello()
module2.class3().hello()  # 通過package1的module2引用到了package2中module3下的class3

請注意其中的注釋。test.py 腳本運行結果如下:

$ python3 test.py 
I am class1 in module1
I am class2 in module2
I am class3 in module3 which of package2

 

根據我們工程的實際情況可能會有很多更復雜的情況,其原理是不變的。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM