函數式編程(模塊基礎)
什麽是模塊?
在計算機程序的開發過程中,隨著程序代碼越寫越多,在一個文件裏代碼就會越來越長,越來越不容易維護。
為了編寫可維護的代碼,我們把很多函數分組,分別放到不同的文件裏,這樣,每個文件包含的代碼就相對較少,很多編程語言都采用這種組織代碼的方式。在Python中,一個.py文件就稱之為一個模塊(Module)。
使用模塊有什麽好處?
- 最大的好處是大大提高了代碼的可維護性。其次,編寫代碼不必從零開始。當一個模塊編寫完畢,就可以被其他地方引用。我們在編寫程序的時候,也經常引用其他模塊,包括Python內置的模塊和來自第三方的模塊。
- 使用模塊還可以避免函數名和變量名沖突。每個模塊有獨立的命名空間,因此相同名字的函數和變量完全可以分別存在不同的模塊中,所以,我們自己在編寫模塊時,不必考慮名字會與其他模塊沖突
模塊分類
模塊分為三種:
- 內置標準模塊(又稱標準庫)執行help(‘modules‘)查看所有python自帶模塊列表
- 第三方開源模塊,可通過pip install 模塊名 聯網安裝
- 自定義模塊
模塊調用
import module
from module import xx
from module.xx.xx import xx as rename
from module.xx.xx import *
註意:模塊一旦被調用,即相當於執行了另外一個py文件裏的代碼
自定義模塊
這個最簡單, 創建一個.py文件,就可以稱之為模塊,就可以在另外一個程序裏導入
模塊查找路徑
發現,自己寫的模塊只能在當前路徑下的程序裏才能導入,換一個目錄再導入自己的模塊就報錯說找不到了, 這是為什麽?
這與導入路徑有關
import sys
print(sys.path)
輸出
[‘‘, ‘/Library/Frameworks/Python.framework/Versions/3.6/lib/python36.zip‘,
‘/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6‘,
‘/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload‘,
‘/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages‘]
python解釋器會按照列表順序去依次到每個目錄下去匹配你要導入的模塊名,只要在一個目錄下匹配到了該模塊名,就立刻導入,不再繼續往後找。
註意列表第一個元素為空,即代表當前目錄,所以你自己定義的模塊在當前目錄會被優先導入。
開源模塊安裝、使用
https://pypi.python.org/pypi 是python的開源模塊庫,截止2017年9.30日 ,已經收錄了118170個來自全世界python開發者貢獻的模塊,幾乎涵蓋了你想用python做的任何事情。 事實上每個python開發者,只要註冊一個賬號就可以往這個平臺上傳你自己的模塊,這樣全世界的開發者都可以容易的下載並使用你的模塊。那如何從這個平臺上下載代碼呢?
1.直接在上面這個頁面上點download,下載後,解壓並進入目錄,執行以下命令完成安裝
編譯源碼 python setup.py build
安裝源碼 python setup.py install
- 直接通過pip安裝
pip3 install paramiko #paramiko 是模塊名
pip命令會自動下載模塊包並完成安裝。
軟件一般會被自動安裝你python安裝目錄的這個子目錄裏
/your_python_install_path/3.6/lib/python3.6/site-packages
pip命令默認會連接在國外的python官方服務器下載,速度比較慢,你還可以使用國內的豆瓣源,數據會定期同步國外官網,速度快好多
sudo pip install -i http://pypi.douban.com/simple/ alex_sayhi --trusted-host pypi.douban.com #alex_sayhi是模塊名
使用
下載後,直接導入使用就可以,跟自帶的模塊調用方法無差,演示一個連接linux執行命令的模塊
#coding:utf-8
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(‘192.168.1.108‘, 22, ‘alex‘, ‘123‘)
stdin, stdout, stderr = ssh.exec_command(‘df‘)
print(stdout.read())
ssh.close();
執行命令 - 通過用戶名和密碼連接服務器
包(Package)
當你的模塊文件越來越多,就需要對模塊文件進行劃分,比如把負責跟數據庫交互的都放一個文件夾,把與頁面交互相關的放一個文件夾,
.
└── my_proj
├── crm #代碼目錄
│ ├── admin.py
│ ├── apps.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
├── manage.py
└── my_proj #配置文件目錄
├── settings.py
├── urls.py
└── wsgi.py
像上面這樣,一個文件夾管理多個模塊文件,這個文件夾就被稱為包
那不同包之間的模塊互相導入呢?
crm/views.py內容
def sayhi():
print(‘hello world!‘)
通過manage.py調用
from crm import views
views.sayhi()
執行manage.py (註意這裏用python2)
Alexs-MacBook-Pro:my_proj alex$ ls
crm manage.py my_proj
Alexs-MacBook-Pro:my_proj alex$ python manage.py
Traceback (most recent call last):
File "manage.py", line 6, in <module>
from crm import views
ImportError: No module named crm
竟然說找不到模塊,為什麽呢?
包就是文件夾,但該文件夾下必須存在 __init__.py 文件, 該文件的內容可以為空。__int__.py用於標識當前文件夾是一個包。
在crm目錄下創建一個空文件__int__.py ,再執行一次就可以了
Alexs-MacBook-Pro:my_proj alex$ touch crm/__init__.py #創建一個空文件
Alexs-MacBook-Pro:my_proj alex$
Alexs-MacBook-Pro:my_proj alex$ ls crm/
__init__.py admin.py models.py views.py
__pycache__ apps.py tests.py views.pyc
Alexs-MacBook-Pro:my_proj alex$ python manage.py
hello world!
註意,在python3裏,即使目錄下沒__int__.py文件也能創建成功,猜應該是解釋器優化所致,但創建包還是要記得加上這個文件 吧。
跨模塊導入
目錄結構如下
.
├── __init__.py
├── crm
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── models.py
│ ├── tests.py
│ ├── views.py
├── manage.py
└── proj
├── __init__.py
├── settings.py
├── urls.py
└── wsgi.py
根據上面的結構,如何實現在crm/views.py
裏導入proj/settings.py
模塊?
直接導入的話,會報錯,說找到不模塊
$ python3 views.py
Traceback (most recent call last):
File "views.py", line 2, in <module>
from proj import settings
ModuleNotFoundError: No module named ‘proj‘
是因為路徑找不到,proj/settings.py 相當於是crm/views.py的父親(crm)的兄弟(proj)的兒子(settings.py),settings.py算是views.py的表弟啦,在views.py裏只能導入同級別兄弟模塊代碼,或者子級別包裏的模塊,根本不知道表弟表哥的存在。這可怎麽辦呢?
答案是添加環境變量,把父親級的路徑添加到sys.path中,就可以了,這樣導入 就相當於從父親級開始找模塊了。
crm/views.py中添加環境變量
import sys ,os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) #__file__的是打印當前被執行的模塊.py文件相對路徑,註意是相對路徑
print(BASE_DIR)
sys.path.append(BASE_DIR)
from proj import settings
def sayhi():
print(‘hello world!‘)
print(settings.DATABASES)
輸出
$ python3 views.py
/Users/alex/Documents/work/PyProjects/luffy_課件/21天入門/chapter4-常用模塊/packages/my_proj
---my proj init--- #proj/__init__.py輸出
in proj/settings.py #proj/settings.py輸出
{‘host‘: ‘localhost‘}
*註意;此時在proj/settings.py寫上import urls會有問題麽?
DATABASES= {
‘host‘:‘localhost‘
}
import urls #這行剛加的
print(‘in proj/settings.py‘)
結果報錯了
ModuleNotFoundError: No module named ‘urls‘
為什麽呢? 因為現在的程序入口是views.py , 你在settings.py導入import urls,其實相當於在crm目錄找urls.py,而不是proj目錄,若想正常導入,要改成如下
DATABASES= {
‘host‘:‘localhost‘
}
from proj import urls #proj這一層目錄已經添加到sys.path裏,可以直接找到
print(‘in proj/settings.py‘)
絕對導入&相對導入
在linux裏可以通過cd ..回到上一層目錄 ,cd ../.. 往上回2層,這個..就是指相對路徑,在python裏,導入也可以通過..
例如:
.
├── __init__.py
├── crm
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── models.py
│ ├── tests.py
│ ├── views.py #from ..proj import settings
├── manage.py
└── proj
├── __init__.py
├── settings.py #from .import urls
├── urls.py
└── wsgi.py
views.py裏代碼
from ..proj import settings
def sayhi():
print(‘hello world!‘)
print(settings.DATABASES)
執行結果報錯了
Traceback (most recent call last):
File "my_proj/crm/views.py", line 4, in <module>
from ..proj import settings
SystemError: Parent module ‘‘ not loaded, cannot perform relative import
或者有人會看到這個錯
ValueError: attempted relative import beyond top-level package
其實這兩個錯誤的原因歸根結底是一樣的:在涉及到相對導入時,package所對應的文件夾必須正確的被python解釋器視作package,而不是普通文件夾。否則由於不被視作package,無法利用package之間的嵌套關系實現python中包的相對導入。
文件夾被python解釋器視作package需要滿足兩個條件:
- 文件夾中必須有__init__.py文件,該文件可以為空,但必須存在該文件。
- 不能作為頂層模塊來執行該文件夾中的py文件(即不能作為主函數的入口)。
所以這個問題的解決辦法就是,既然你在views.py裏執行了相對導入,那就不要把views.py當作入口程序,可以通過上一級的manage.py調用views.py
.
├── __init__.py
├── crm
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── models.py
│ ├── tests.py
│ ├── views.py #from ..proj import settings
├── manage.py #from crm import views
└── proj
├── __init__.py
├── settings.py #from .import urls
├── urls.py
└── wsgi.py
事實證明還是不行,報錯
ValueError: attempted relative import beyond top-level package
但把from ..proj import settings
改成from . import models
後卻執行成功了,為什麽呢?
from .. import models
會報錯的原因是,這句代碼會把manage.py所在的這一層視作package,但實際上它不是,因為package不能是頂層入口代碼,若想不出錯,只能把manage.py往上再移一層。
正確的代碼目錄結構如下
packages/
├── __init__.py
├── manage.py #from my_proj.crm import views
└── my_proj
├── crm
│ ├── admin.py
│ ├── apps.py
│ ├── models.py
│ ├── tests.py
│ ├── views.py #from . import models; from ..proj import settings
└── proj
├── __init__.py
├── settings.py
├── urls.py
└── wsgi.py
再執行manage.py就不會報錯了。
註:雖然python支持相對導入,但對模塊間的路徑關系要求比較嚴格,處理不當就容易出錯,so並不建議在項目裏經常使用。
函數式編程(模塊基礎)