這是一篇舊文,編寫於2017年,用於公司內部交流。整理更新後於2019年04月06日重發,內容有效期樂觀估計半年,閱讀請注意保質期。
為什麼用python虛擬環境
python版本差異
python當前主要有2個release版本 Python 2.7.16 和 Python 3.7.3 ,這兩個版本在一些語法上存在較大差異。
如果你從網上下載了一段python程式碼,卻執行不起來,首先要排除的是python版本問題。比如下面這個 hello_python.py ,程式碼非常簡單,就一句列印 hello,world
到螢幕上。
print "hello,world"
複製程式碼
如果你使用python2.7,恭喜你可以很好的跑起來,如下:
(python27) ➜ python hello_python.py
hello,world
複製程式碼
如果你使用的是python3,可能就實現從入門到放棄:(
(python37) ➜ python hello_python.py
File "hello_python.py", line 3
print "hello,world"
^
SyntaxError: Missing parentheses in call to 'print'. Did you mean print("hello,world")?
複製程式碼
python3 中讓程式碼正常執行的辦法和提示一樣,修改語句為
print("hello,world")
python2.7已經 “過時”,pip執行是會提示:
DEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 won't be maintained after that date. A future version of pip will drop support for Python 2.7.
複製程式碼
現實狀況卻是一些庫的版本問題或者歷史遺留問題,可能需要在python2和python3之間切換。
python使用場景差異
python語言應用非常廣泛,涉及伺服器指令碼、爬蟲、程式開發、科學計算,大資料,機器學習等。不同的場景下,使用的庫是有差異:
使用場景 | 常用庫 |
---|---|
程式開發 | flask/django |
爬蟲 | requests/beautifulsoup4/scrapy |
科學計算 | pandas/numpy/matplotlib |
... | ... |
庫又會依賴另外的庫,這樣如果全部安裝在一個環境裡,難以規避庫的版本衝突。
鑑於python版本差異和使用場景差異,推薦使用虛擬環境進行隔離管理,省事不少。
怎麼使用虛擬環境
使用 pip
使用虛擬環境之前,我們先花一點點時間來了解python的包安裝工具 pip , 相信我這很簡單。
Python的最大的優勢之一是豐富的庫,跨平臺,在UNIX,Windows和Macintosh相容很好。
安裝這些庫,讓開發速度飈起來,就需要使用 pip。
下面使用pip
安裝requests
庫示例:
(py27studio) ➜ pytest pip install requests
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Collecting requests
Downloading https://pypi.tuna.tsinghua.edu.cn/packages/f1/ca/10332a30cb25b627192b4ea272c351bce3ca1091e541245cccbace6051d8/requests-2.20.0-py2.py3-none-any.whl (60kB)
100% |████████████████████████████████| 61kB 1.5MB/s
Collecting chardet<3.1.0,>=3.0.2 (from requests)
Using cached https://pypi.tuna.tsinghua.edu.cn/packages/bc/a9/01ffebfb562e4274b6487b4bb1ddec7ca55ec7510b22e4c51f14098443b8/chardet-3.0.4-py2.py3-none-any.whl
Collecting urllib3<1.25,>=1.21.1 (from requests)
Downloading https://pypi.tuna.tsinghua.edu.cn/packages/62/00/ee1d7de624db8ba7090d1226aebefab96a2c71cd5cfa7629d6ad3f61b79e/urllib3-1.24.1-py2.py3-none-any.whl (118kB)
100% |████████████████████████████████| 122kB 1.5MB/s
Collecting certifi>=2017.4.17 (from requests)
Downloading https://pypi.tuna.tsinghua.edu.cn/packages/56/9d/1d02dd80bc4cd955f98980f28c5ee2200e1209292d5f9e9cc8d030d18655/certifi-2018.10.15-py2.py3-none-any.whl (146kB)
100% |████████████████████████████████| 153kB 287kB/s
Collecting idna<2.8,>=2.5 (from requests)
Downloading https://pypi.tuna.tsinghua.edu.cn/packages/4b/2a/0276479a4b3caeb8a8c1af2f8e4355746a97fab05a372e4a2c6a6b876165/idna-2.7-py2.py3-none-any.whl (58kB)
100% |████████████████████████████████| 61kB 131kB/s
Installing collected packages: chardet, urllib3, certifi, idna, requests
Successfully installed certifi-2018.10.15 chardet-3.0.4 idna-2.7 requests-2.20.0 urllib3-1.24.1
複製程式碼
從示例可見 pip 的2個特點:
-
快速安裝。命令列中一鍵安裝完成。
-
自動解決依賴。在安裝requests的同時還安裝了: chardet, urllib3, certifi, idna 並且自動解決各個庫的版本問題, 一般情況下我們不用關心這些細節。
下面是 pip的一些常用命令
# 列出庫列表
pip list
# 升級pip
pip install pip -U
# 安裝指定的庫
pip install name
# 刪除指定的庫
pip uninstall name
複製程式碼
瞭解 pip 的常用命令後,可以愉快的寫程式碼了。逐漸你會遇到在不同機器上同步庫或者同事使用你的程式碼,需要安裝相同的庫。這時候你就需要pip的2個進階命令了。
# 將當期環境中的庫列表形成 requirements.txt 檔案
pip freeze >requirements.txt
# 根據 requirements.txt 批量安裝庫
pip install -r requirements.txt
複製程式碼
你可以使用下面2個命令,獲取pip的使用幫助,瞭解更多
pip help
pip install -h
複製程式碼
由於PyPI服務位於國外,訪問起來比較緩慢,可以使用國內的一些源進行加速
設定pip使用國內源
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
複製程式碼
一些其它的源: 豆瓣源:pypi.douban.com/simple/ 阿里雲源: mirrors.aliyun.com/pypi/simple…
其它平臺/語言也有類似的解決方案,如果你有興趣可以使用下面關鍵字自行了解。
yum/apt/npm/brew/maven...
使用 venv
Python 從3.3 版本開始,自帶了一個虛擬環境 venv,在 PEP-405 中可以看到它的詳細介紹。
在 *nix 系統上,可以直接執行 pyvenv ./blog-env
來建立一個虛擬環境。在 Windows 系統上,則可以使用 python -m venv ./blog-env
來建立, 這個命令也可以用於 *nix 系統,推薦使用。
venv建立的虛擬環境,建議就在專案目錄,這樣使用起來更方便,沒有記憶負擔。
建立完成後 *nix 系統上, 使用 source ./blog-env/bin/activate
進入虛擬環境,window系統上,使用 Scripts\\activate.bat
,效果如下:
➜ blog source ./blog-env/bin/activate
(blog-env) ➜ blog
複製程式碼
注意在命令提示符之前新出現的 (blog-env) 標識進入了新的虛擬環境。
這樣很方便就獲得了一個隔離的python環境。
使用 pipenv
pipenv是Kenneth Reitz推出的python包依賴工具, kr就是大名鼎鼎requests的作者。pipenv號稱Python Dev Workflow for Humans, 實力可見一斑。
介紹pipenv之前,我們先看venv存在的問題。我這裡有一個django專案,使用django-rest-framework提供RestFul api服務, 利用jwt進行前後端認證,專案安裝完後依賴大概如下:
(wxsc) ➜ wordpress pip list
Package Version
----------------------- ----------
asn1crypto 0.24.0
certifi 2018.10.15
cffi 1.11.5
chardet 3.0.4
coreapi 2.3.3
coreschema 0.0.4
cryptography 2.3.1
diff-match-patch 20181111
Django 2.0.5
django-crispy-forms 1.7.2
django-filter 2.0.0
django-formtools 2.1
django-import-export 1.1.0
django-reversion 3.0.0
djangorestframework 3.9.0
djangorestframework-jwt 1.11.0
et-xmlfile 1.0.1
future 0.16.0
httpie 1.0.0
httplib2 0.12.0
idna 2.7
itypes 1.1.0
jdcal 1.4
Jinja2 2.10
Markdown 3.0.1
MarkupSafe 1.1.0
odfpy 1.3.6
openpyxl 2.5.10
pip 18.1
pipenv 2018.11.14
pycparser 2.19
Pygments 2.2.0
PyJWT 1.6.4
PyMySQL 0.9.2
pytz 2018.5
PyYAML 3.13
requests 2.20.1
setuptools 40.6.2
six 1.11.0
tablib 0.12.1
unicodecsv 0.14.1
uritemplate 3.0.0
urllib3 1.24.1
virtualenv 16.1.0
virtualenv-clone 0.4.0
wheel 0.32.2
xlrd 1.1.0
XlsxWriter 1.1.2
xlwt 1.3.0
複製程式碼
前前後後大約安裝了50個包,最後完全不清楚,那些是我直接安裝的,那些是間接安裝的,pipenv可以解決這個問題。
pipenv 安裝很方便:
$ pip install pipenv
複製程式碼
如果標準版本還是python2.7,則可以使用 pip3 install pipenv
pipenv使用起來也很方便。接下來我們一起了解這個過程。
建立一個新的工作目錄myproject
,使用下面命令建立虛擬環境,建立完成後目錄下會生成一個Pipfile檔案。
$ pipenv --three
Creating a virtualenv for this project…
Pipfile: /private/tmp/myproject/Pipfile
Using /Library/Frameworks/Python.framework/Versions/3.7/bin/python3 (3.7.1) to create virtualenv…
✔ Complete
Using base prefix '/Library/Frameworks/Python.framework/Versions/3.7'
New python executable in /Users/tu/codes/venv/myproject-7Ev2diGY/bin/python3
Also creating executable in /Users/tu/codes/venv/myproject-7Ev2diGY/bin/python
Installing setuptools, pip, wheel...
done.
Running virtualenv with interpreter /Library/Frameworks/Python.framework/Versions/3.7/bin/python3
Virtualenv location: /Users/tu/codes/venv/myproject-7Ev2diGY
Creating a Pipfile for this project…
複製程式碼
檢視虛擬環境的實際目錄,瞭解python命令路徑(可以用於配合pycharm選擇直譯器)。
$ pipenv --venv
$ pipenv --py
複製程式碼
pipenv 的虛擬環境會統一存放,不會在專案路徑下
安裝軟體包。包安裝完成後,會在專案路徑下生成Pipfile.lock檔案。
$ pipenv install requests
$ pipenv install django
複製程式碼
注意: 這裡不是使用 pip 進行包安裝,而是使用 pipenv 。
檢視專案依賴。檢視比較清晰的描述了,專案依賴了2個包,每個包又分別依賴了其它的一些包。
➜ myproject2 pipenv graph
Django==2.1.3
- pytz [required: Any, installed: 2018.7]
requests==2.20.1
- certifi [required: >=2017.4.17, installed: 2018.10.15]
- chardet [required: >=3.0.2,<3.1.0, installed: 3.0.4]
- idna [required: >=2.5,<2.8, installed: 2.7]
- urllib3 [required: >=1.21.1,<1.25, installed: 1.24.1]
複製程式碼
重用requirements.txt
檔案。在專案目錄下建立requirements.txt
,內容參見上文。
$ pipenv install
複製程式碼
pipenv 預設會讀取本地目錄,也可以使用**-r**引數指定
requirements.txt
位置。
pipenv 的依賴和 venv 有差異,可能直接匯入 venv中的
requirements.txt
有一些問題,可以直接手工安裝。
pipenv就簡單介紹到這裡,大家可以看pipenv使用指南。總的來說venv和pipenv各有優勢吧,venv原生自帶,簡單方便,比較適合伺服器上除錯;pipenv,功能強大更適合本地除錯。
Anaconda
科學計算領域,python著名的庫pandas,numpy和matplotlib, 然後還有影像處理和視覺這塊的opencv,以及機器學習的tensorflow安裝比較複雜,在這個領域一般使用anaconda的解決方案,其wiki介紹如下:
Anaconda 是一種Python語言的免費增值開源發行版,用於進行大規模資料處理、預測分析,和科學計算,致力於簡化包的管理和部署。 Anaconda使用軟體包管理系統Conda進行包管理。
我選擇的是anaconda3,使用的iterm2,安裝完成後會有路徑問題,需要在~/.zshrc
中新增:
export PATH="/anaconda3/bin:$PATH"
複製程式碼
然後訪問使用下面命令檢視anaconda環境。
➜ ~ which conda
/anaconda3/bin/conda
➜ ~ conda --version
conda 4.5.11
複製程式碼
建立虛擬環境,並進入虛擬環境:
conda create --name python36 python=3.6
➜ ~ source activate python36
(python36) ➜ ~
(python36) ➜ ~ python -V
Python 3.6.7 :: Anaconda, Inc.
(python36) ➜ ~ source deactivate
➜ ~
複製程式碼
檢視conda管理的環境列表:
➜ ~ conda info -e
# conda environments:
#
base * /anaconda3
python36 /anaconda3/envs/python36
複製程式碼
Anaconda在個人研發過程中使用不多,如果想了解更多,可以看Anaconda 入門安裝教程。
docker時代的python環境
前面介紹的內容,都是在本地進行一些配置,建立各種環境。可是本地就一個,折騰幾次後還是容易亂,docker這麼強大,我們也可以用docker來玩python開發。
首先,我們編寫hello.py
,程式碼如下:
# -*- coding:utf-8 -*-
def test():
print "Hello, python, docker"
if __name__ == "__main__":
test()
複製程式碼
然後執行下面命令:
docker run -it --rm --name docker-python2 -v "$PWD":/usr/src/myapp -w /usr/src/myapp python:2.7.16-alpine3.9 python hello.py
複製程式碼
命令結果如下:
Unable to find image 'python:2.7.16-alpine3.9' locally
2.7.16-alpine3.9: Pulling from library/python
8e402f1a9c57: Already exists
d8cdc394c05b: Pull complete
1e2b4a75cc6b: Pull complete
2fbbf60d928e: Pull complete
Digest: sha256:46d6e67d464a2811efe60440248623b14cb6db273fac59631dd8bd9e13a77491
Status: Downloaded newer image for python:2.7.16-alpine3.9
Hello, python, docker
複製程式碼
注意,初次執行會進行映象檔案下載,比較緩慢,下載成功後再次執行指令碼就會非常迅速了。
這裡的docker命令比較複雜,可以先不用管它的含義。我們繼續編寫hello3.py
,程式碼如下:
# -*- coding:utf-8 -*-
def test():
print("Hello, python, docker")
if __name__ == "__main__":
test()
複製程式碼
使用下面命令執行:
docker run -it --rm --name docker-python3 -v "$PWD":/usr/src/myapp -w /usr/src/myapp python:3.7.3-alpine3.9 python hello3.py
複製程式碼
執行結果如下:
Unable to find image 'python:3.7.3-alpine3.9' locally
3.7.3-alpine3.9: Pulling from library/python
Digest: sha256:11568bb68bd375727e468ea5995f556139ff305eed9d8ee1d04b1a4a03a6486a
Status: Downloaded newer image for python:3.7.3-alpine3.9
Hello, python, docker
複製程式碼
我們簡單瞭解一下執行命令的含義:
➜ python docker run --help
Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
-i, --interactive Keep STDIN open even if not attached
...
--rm Automatically remove the container when it exits
...
-t, --tty Allocate a pseudo-TTY
...
-v, --volume list Bind mount a volume
--volume-driver string Optional volume driver for the container
--volumes-from list Mount volumes from the specified container(s)
-w, --workdir string Working directory inside the container
複製程式碼
docker run -it --rm --name docker-python2 -v "$PWD":/usr/src/myapp -w /usr/src/myapp python:2.7.16-alpine3.9 python hello.py
翻譯過來的意思大概是下面幾點:
- 使用 python:2.7.16-alpine3.9 映象啟動容器,python表示映象名稱,2.7.16-alpine3.9表示版本標籤。
- 容器名稱命名為docker-python2,並且輸出資訊到當前終端,容器執行完成後自動刪除。
- 容器使用資料卷,將當前目錄和容器內的
/usr/src/myapp
目錄進行對映。 - 設定
/usr/src/myapp
為工作目錄,然後執行python hello.py
命令。
這樣經過上面幾步,我們就使用docker容器執行了hello.py
了。而且我們也有了2個映象,可以分別執行python2和python3的程式碼。當然實際的工程中,只是執行一個py檔案
有點殺雞用牛刀的感覺,可以如果執行一個django專案,卻是可以很好的保持開發環境和部署環境的一致。這部分內容,以後我會進行補充介紹。
提示:docker和venv/pipenv也可以配合使用,將虛擬環境建立在當前目錄即可,留給大家動手探索吧。
回顧
最後,我們一起來簡單回顧一下python虛擬環境:
- 使用python虛擬環境可以有效解決python語法版本差異及使用場景差異。
- 使用pip安裝各種包。
- python3自帶venv建立和管理各種環境。
- pipenv是一種混合了pip和venv的環境管理方法。
- 使用anaconda管理python科學計算環境。
- 可以使用docker等方式使用python環境。
附錄
如果繼續使用pyhon2,可以參靠下面部分內容。python2的虛擬環境 virtualenv&virtualenvwrapper。
virtualenv 是一個建立隔絕的Python環境的工具,可以建立一個包含所有必要的可執行檔案的資料夾,用來使用Python工程所需的包。
virtualenv安裝非常簡單,直接使用下面命令:
pip install virtualenv
複製程式碼
可能會遇到許可權問題,可以使用sudo提權
但是virtualenv有一個弊端是,每次會在當前目錄建立venv)(pycharm預設使用這種方式),每次需要自己記住不同的虛擬機器目錄,使用起來不太方便。 這裡我們直接跳過virtualenv的使用,繼續使用其擴充套件包virtualenvwrapper
virtualenvwrapper安裝也非常簡單,直接使用下面命令:
pip install virtualenvwrapper
複製程式碼
! 注意:windows使用者使用的命令是 pip install virtualenvwrapper-win 後面帶**-win**
使用virtualenvwrapper需要先設定環境變數,我使用的iTerm2
配置的zshrc
,需要在使用者根目錄下的.zshrc
中增加下面2行:
export WORKON_HOME=~/codes/venv
source /usr/local/bin/virtualenvwrapper.sh
複製程式碼
配置好環境變數後,退出重啟term
生效。
建立一個flask開發環境
mkvirtualenv flask
Using base prefix '/Library/Frameworks/Python.framework/Versions/2.7'
New python executable in /Users/tu/codes/venv/flask/bin/python
Also creating executable in /Users/tu/codes/venv/flask/bin/python
Installing setuptools, pip, wheel...done.
virtualenvwrapper.user_scripts creating /Users/tu/codes/venv/flask/bin/predeactivate
virtualenvwrapper.user_scripts creating /Users/tu/codes/venv/flask/bin/postdeactivate
virtualenvwrapper.user_scripts creating /Users/tu/codes/venv/flask/bin/preactivate
virtualenvwrapper.user_scripts creating /Users/tu/codes/venv/flask/bin/postactivate
virtualenvwrapper.user_scripts creating /Users/tu/codes/venv/flask/bin/get_env_details
(flask) ➜ pytest
複製程式碼
注意建立好虛擬環境後會自動進入該環境,命令提示符前會顯示當前環境的名稱,例如(flask) 字樣。
再建立一個爬蟲的開發環境
mkvirtualenv spider
...
複製程式碼
這樣你可以在flask的環境中安裝flask庫,spider的環境中安裝requests庫,而不用擔心依賴衝突。
如果你希望建立一個python3版本的環境,需要先行安裝好python3,然後使用下面命令:
# -p 引數指定版本,可以使用python3的絕對路徑
mkvirtualenv py3studio -p python3
複製程式碼
其它常用命令,如下:
# 列出env列表
workon
# 進入指定的env
workon name
# 退出當前env
deactivate
# 刪除env
rmvirtualenv name
複製程式碼
ok,我們使用pip安裝virtualenv和virtualenvwrapper,順利的解決了不同版本的python問題,可以愉快的開發(玩耍)了。