65.9K
CodeProject 正在变化。 阅读更多。
Home

关于 Python、Virtualenv、VSC 集成及其他的一些说明

starIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

1.00/5 (2投票s)

2020年1月28日

CPOL

10分钟阅读

viewsIcon

6472

downloadIcon

100

这是一篇关于 Python、Virtualenv、VSC 集成及其他主题的说明。

背景

Python 被认为是一种易学易用的语言,但本文并非关于 Python 语言。它是关于创建一个隔离环境,以便我们能够确定地运行 Python 程序。

在本文中,我附带了几个简单的 Python 文件。我将使用它们向您展示拥有一个隔离环境来运行 Python 程序的好处。顺便说一句,如果您使用 Ubuntu 风味的 Linux,这个链接 https://launchpad.net/~deadsnakes/+archive/ubuntu/ppa 对您来说是一个很棒的链接。

有多少个 Python?

要运行 Python 程序,我们通常需要在计算机上拥有三个组件。

  1. 一个 Python 解释器
  2. 一个 Python 包安装程序(pip)实例,用于安装依赖包
  3. 一个搜索路径,解释器会在其中查找运行我们程序所需的依赖包

多个解释器

Python 宣称的优势之一是它在所有计算机上都随时可用。这也意味着我们可能在不知情的情况下拥有一个 Python 解释器版本。在我的 Linux Mint 17.2 Cinnamon 64 位系统中,我至少有 3 个 Python 解释器。

当我们安装操作系统时,它会附带一些预安装的 Python 解释器。这些 Python 解释器对于操作系统的正常运行很重要。我们不应该在不完全了解自己在做什么的情况下将其删除。

多个包安装程序

为了让事情更复杂,我们的计算机上实际上有多个 pip 实例。

多个搜索路径

当我们运行 Python 程序时,我们需要确保使用了正确的解释器实例。让我们看看 python-search-path.py 文件。

# https://docs.pythonlang.cn/3/tutorial/modules.html#the-module-search-path
import sys
    
for item in sys.path:
  print(item)

如果我们使用以下命令运行程序,我们可以找到 python 解释器使用的包搜索路径。

python python-search-path.py

如果我们使用 python3.4 解释器运行它,我们将看到一个完全不同的路径。

python3.4 python-search-path.py 

现在我们知道计算机上的 Python 版本很复杂。更不稳定的是,我们可能正在使用与操作系统相同的 Python 版本。如果我们更新某个操作系统也使用的包,我们可能会给操作系统带来问题。在最坏的情况下,它甚至可能无法启动。我个人在玩某些 Python 版本后遇到过操作系统问题。

虚拟环境

我们可以努力确保我们使用所需的 Python 解释器。我们也可以努力将包安装到正确的搜索路径中,以使 Python 程序顺利运行。但我们也可以创建一个 虚拟环境,轻松而确定地运行 Python 程序。

  • 虚拟环境确保我们使用所需的 Python 解释器。
  • 虚拟环境确保我们使用所需的 Python 包安装程序 (pip)。
  • 虚拟环境确保我们在环境中安装包,并在运行程序时在环境中查找它们。

物理上,虚拟环境是一个文件夹。这个文件夹包含了运行 Python 程序所需的一切。当我们安装一个包时,它也会被安装到这个文件夹中。

创建虚拟环境

要创建虚拟环境,我们需要安装 virtualenv 工具。在我的计算机上,我使用以下命令安装 virtualenv

sudo python3 -m pip install virtualenv

然后我们可以验证安装是否成功。

看起来 virtualenv 独立于任何特定版本的 Python。安装 virtualenv 后,我们可以使用以下命令创建虚拟环境

virtualenv -p /usr/bin/python3.4 environment-3.4
  • 在创建虚拟环境时,我们指定了 Python 解释器的执行路径,它将成为我们使用虚拟环境时的 Python 解释器。
  • 在创建虚拟环境时,我们指定了一个文件夹名称。它是用于保存虚拟环境的物理文件夹。

激活虚拟环境

要激活虚拟环境,我们可以发出以下命令

source environment-3.4/bin/activate

激活虚拟环境后,我们可以检查 Python 和 pip 的执行路径。

python python-search-path.py

激活虚拟环境后,我们可以看到所有的 python、pip 和搜索路径都位于虚拟环境文件夹中。我们可以使用以下命令停用虚拟环境

deactivate

如果我们要完全删除虚拟环境,只需删除 environment-3.4 文件夹即可。

在虚拟环境中安装包

出于实验目的,我在 python-flask-api.py 文件中创建了一个小的 flask 应用程序。

from flask import Flask, request, jsonify
    
app = Flask(__name__)
    
@app.route('/')
def message():
  name = 'Song Li'
  return jsonify(
    username = name,
    email = 'song.li@email.com',
  )
    
if __name__ == '__main__':
   app.run(host='0.0.0.0', port=3000)

此应用程序需要 flask 包。激活虚拟环境后,我们可以使用以下命令安装包

python -m pip install flask

安装 flask 后,我们可以运行 python-flask-api.py 文件。

python python-flask-api.py 

我们可以通过运行 python-inspect-path.py 文件进一步验证 flask 包是否安装在虚拟环境中。

import inspect
from flask import Flask
    
print(inspect.getfile(Flask))

创建 virtualenv 的另一种方法是使用 venv。这是一篇关于 venv 的好文档。在 Ubuntu 中,我们需要手动安装 python3.9-venv(或相应版本的 venv)。

apt-cache policy python3.9-venv

然后我们可以使用一个简单的命令创建 virtualenv。

python3.9 -m venv "virtualenv-directory-name"

我们可以参考 https://docs.pythonlang.cn/3/library/venv.html 以获取 venv 更高级的用法。

PIP Freeze

在虚拟环境中工作一段时间后,您可能已经安装了许多包。如果您想让另一个人运行您的程序,您需要告诉他们这些包和版本。激活虚拟环境后,您可以发出以下命令

python -m pip freeze > requirements.txt 

它会创建一个名为 requirements.txt 的文件,其中包含虚拟环境中所有包的信息。

Click==7.0
Flask==1.0.4
itsdangerous==1.1.0
Jinja2==2.10.3
MarkupSafe==1.1.1
Werkzeug==0.16.1

我们可以使用以下命令在新虚拟环境中安装所有包

python -m pip install -r requirements.txt

我们也可以使用以下命令删除 requirements.txt 文件中的所有包

python -m pip uninstall -r requirements.txt -y

pip freeze 命令接受 --path 参数,它将 requirements.txt 文件限制为包含安装在特定路径中的包。

python3 -m pip freeze --path .  > requirements.txt

pip install 命令接受 -t--target 参数,将包安装到指定的路径。

python3 -m pip install -r requirements.txt -t .

Jupyter

使用 Python 非常方便。这是关于 Jupyter 的文档。我们可以在虚拟环境中安装 Jupyter。

python3.9 -m venv env-3.9
source env-3.9/bin/activate
pip install pip -U

上述命令创建一个虚拟环境并在其中升级 pip。我们可以使用以下命令验证虚拟环境

which python
pip --version

我们可以使用以下命令安装 Jupyter

pip install jupyterlab

我们可以使用以下命令启动 Jupyter

jupyter-lab

如果我们要将 ipynb 文件转换为常规 python 文件,可以使用以下命令

python -m nbconvert --to script --no-prompt Untitled.ipynb

我们可以使用以下命令检查是否安装了 nbconvert

pip list | grep nbconvert

VSC 集成

Visual Studio Code 是一个不错的 Python 程序 IDE。如果您不熟悉 VSC,可以查看 我之前的说明。为了支持 Python,我只需要安装 Microsoft 的 Python 扩展。

为了能够使用虚拟环境的 Python 解释器运行和调试程序,我们只需创建如下所示的 launch.json 文件。它将 pythonPath 指向虚拟环境。

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "python-search-path.py",
      "pythonPath": "${workspaceFolder}/environment-3.4/bin/python",
      "type": "python",
      "request": "launch",
      "program": "${workspaceFolder}/python-search-path.py",
      "console": "integratedTerminal"
    },
    {
      "name": "python-inspect-path.py",
      "pythonPath": "${workspaceFolder}/environment-3.4/bin/python",
      "type": "python",
      "request": "launch",
      "program": "${workspaceFolder}/python-inspect-path.py",
      "console": "integratedTerminal"
    },
    {
      "name": "python-flask-api.py",
      "pythonPath": "${workspaceFolder}/environment-3.4/bin/python",
      "type": "python",
      "request": "launch",
      "program": "${workspaceFolder}/python-flask-api.py",
      "console": "integratedTerminal"
    }
  ]
}

如果您愿意,实际上可以将 python 可执行文件的路径添加到 settings.json 文件中。

{
  "files.exclude": {
    "**/.git": true,
    "**/.gitignore": true,
    "**/environment-3.4": true
  },
  "python.pythonPath": "${workspaceFolder}/environment-3.4/bin/python3.4"
}

在这种情况下,无论何时打开终端,虚拟环境都会自动激活。

Flask 和 Gunicorn

FlaskDjango 是 Python 环境中的 Web 框架。python-flask-api.py 是一个简单的 Flask 应用程序。

import sys, os
from flask import Flask, jsonify
    
app = Flask(__name__)
    
@app.route('/')
def message():
  return jsonify(pid = os.getpid())
    
if __name__ == '__main__':
   app.run(host='0.0.0.0', port=3000)

我们可以使用以下命令启动应用程序

python python-flask-api.py

但当我们启动应用程序时,我们看到了以下警告。

我们需要找到一个好的服务器来托管应用程序。我们有很多选择,但 Gunicorn 是一个受欢迎的选择。我们实际上可以在虚拟环境中安装 Gunicorn

python -m pip install gunicorn

安装 Gunicorn 后,我们可以在虚拟环境中运行它。

gunicorn --bind 0.0.0.0:8000 python-flask-api:app

如果一切顺利,我们现在可以通过 8000 端口号访问应用程序。我们还可以添加 -w 选项来指定工作进程的数量。通常,服务器中每个核心的工作进程数量为 2-4 个。

gunicorn --bind 0.0.0.0:8000 -w 4 python-flask-api:app 

重要的是要知道 Flask 本身可以多线程运行(参考)。

if __name__ == '__main__':
    app.run(threaded=True)

if __name__ == '__main__':
    app.run(threaded=False, processes=3)

Nginx 负载均衡

Nginx 是一个流行的负载均衡器。通常使用 Nginx 来负载均衡多个 Flask 实例。要在我的 Linux Mint 计算机上安装 Nginx,我们可以使用以下命令。

apt-cache policy nginx
sudo apt-get install nginx

安装后,我们可以使用以下命令来操作 Nginx 服务器。

sudo service nginx start
sudo service nginx restart
sudo service nginx reload
sudo service nginx stop
service nginx status

因为我正在我的个人电脑上试验 Nginx,我不想让它在电脑启动时启动,所以我使用以下命令禁用该服务。

sudo update-rc.d nginx disable

为了设置负载均衡,我创建了一个名为 loadbalance.conf 的文件。

upstream pythonweb {
  server localhost:4000;
  server localhost:3000;
  server localhost:2000;
}
    
# This balances all the requests
# It also disable caching
server {
  listen 80;
    
  location / {
    proxy_pass "http://pythonweb/";
    add_header Cache-Control
                'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
  }
}
    
# nginx.conf file
# Comment out this line - include /etc/nginx/sites-enabled/*;
#include /home/song/sandbox/p-virtualenv-excercise/loadbalance.conf;

在我的计算机上,我需要将此文件“包含”到 /etc/nginx/nginx.conf 文件中,并注释掉默认的 /etc/nginx/sites-enabled/*

#include /etc/nginx/sites-enabled/*;
include /home/song/sandbox/p-virtualenv-excercise/loadbalance.conf;

配置准备就绪后,我们可以重启或重载 Nginx 服务器。我们还需要在不同的终端上启动 3 个 Flask 应用程序实例,端口号分别为 2000/3000/4000。以下是在端口号 2000 上启动实例的命令。

gunicorn --bind 0.0.0.0:2000 python-flask-api:app

如果一切顺利,我们就可以通过端口号 80 访问应用程序,请求会在 3 个 Flask 实例之间进行负载均衡。

Python 类与面向对象

Python 支持类和面向对象。在本节中,我们将使用一些示例来快速总结 Python 类和面向对象。

示例 1 - Python 空类和动态属性

# Python allows empty class
class EmptyClass:
  pass
    
# Create an instance of the class
obj_1 = EmptyClass()
    
# Attrubutes can be added to an object dynamically
obj_1.attribute1 = "The attribute 1"
obj_1.attribute2 = "The attribute 2"
    
print(obj_1.attribute1 + ' - ' + obj_1.attribute2)
    
# The __dict__ has all the information
print('The __dict__:')
print(obj_1.__dict__)
    
# Attributes can be deleted from an object
del obj_1.attribute2
print(obj_1.__dict__)
print()

与其他语言中的类不同,Python 类不需要任何实现。

  • 实例属性可以动态添加到 Python 对象中;
  • 实例属性(包括 __dict__ 属性)也可以动态地从对象中删除;
  • __dict__ 属性维护 Python 对象的所有信息。

运行以上程序,我们可以看到以下结果

The attribute 1 - The attribute 2
The __dict__:
{'attribute1': 'The attribute 1', 'attribute2': 'The attribute 2'}
{'attribute1': 'The attribute 1'}

示例 2 - Python 构造函数和实例方法

# A class with an instructor and an instance method
class AClass:
  def __init__(self, attr_1, attr_2):
    self.attr_1 = attr_1
    self.attr_2 = attr_2
    
  def print_attributes(self):
    print("{} - {}".format(self.attr_1, self.attr_2))
    
# Create two instances
obj_1 = AClass('obj_1_a1', 'obj_1_a2')
obj_2 = AClass('obj_2_a1', 'obj_2_a2')
    
# Call the instance method
# Each object has its own instance attributes
obj_1.print_attributes()
obj_2.print_attributes()
print()

尽管我们可以向 Python 对象添加动态属性,但初始化属性最常见的方法是通过构造函数。

  • Python 构造函数名为 __init__,构造函数的第一个参数是对象的引用;
  • Python 实例方法也将其第一个参数作为对象的引用。按照惯例,此参数命名为 self
  • 当调用构造函数和实例方法时,Python 会隐式传入 self 参数。

运行以上程序,我们可以看到以下结果

obj_1_a1 - obj_1_a2
obj_2_a1 - obj_2_a2

示例 3 - 类属性

# A class with both instance and class attributes
class AClass:
  a_class_attr = 'The class attribute'
    
  def print_class_attribute_by_self_reference(self):
    print(self.a_class_attr)
    
  def print_class_attribute_by_class_name(self):
    print(AClass.a_class_attr)
    
# Create an instance of AClass
obj_1 = AClass()
    
# A class attribute can be accessed by
# both the instance reference and the class name
obj_1.print_class_attribute_by_self_reference()
obj_1.print_class_attribute_by_class_name()
print()
    
# Add a instance attribute with the same name
obj_1.a_class_attr = 'This can override the class attribute'
obj_1.print_class_attribute_by_self_reference()
obj_1.print_class_attribute_by_class_name()
print()
    
# Delete the instance attribute
del obj_1.a_class_attr
obj_1.print_class_attribute_by_self_reference()
obj_1.print_class_attribute_by_class_name()
print()

除了实例属性,Python 类还可以有类级别属性。

  • 类属性可以通过对象引用和类名访问;
  • 如果对象有一个与类属性同名的实例属性,并且该属性通过对象引用访问,则实例属性会覆盖类属性。

运行以上程序,我们可以看到以下结果。

The class attribute
The class attribute
    
This can override the class attribute
The class attribute
    
The class attribute
The class attribute

示例 4 - 类方法和静态方法

# A class with an instance method, a class method,
# and a static method
class AClass:
  a_class_attr = 'The class attribute'
    
  def __init__(self):
    self.a_instance_attr = 'The instance attribute'
    
  def an_instance_method(self):
    print("From the instance method - {}".format(self.a_instance_attr))
    
  @classmethod
  def a_class_method(cls):
    print("From the class method - {}".format(cls.a_class_attr))
      
  @staticmethod
  def a_static_mathod():
    print('Print from a static method')
    
    
# Create an instance of AClass and call the methods
obj_1 = AClass()
obj_1.an_instance_method()
obj_1.a_class_method()
obj_1.a_static_mathod()
print()
    
# Class and static methods can be accessed by the class name
AClass.a_class_method()
AClass.a_static_mathod()

一个 Python 类可以有类方法和静态方法。

  • 使用 @classmethod 注解一个方法使其成为类方法。当该方法被调用时,类的引用会作为第一个参数隐式传入。按照惯例,该参数命名为 cls
  • 使用 @staticmethod 注解一个方法使其成为 static 方法。当该方法被调用时,对象引用和类引用都不会隐式传入。

运行以上程序,我们可以看到以下结果。

From the instance method - The instance attribute
From the class method - The class attribute
Print from a static method
    
From the class method - The class attribute
Print from a static method

示例 5 - 继承

# The parent class
class ParentClass:
  def __init__(self):
    self.parent_attr = 'Parent Attribute'
    
  def print(self):
    print("Print from parent class - {}".format(self.parent_attr))
    
# The child class
class ChildClass(ParentClass):
  def __init__(self):
    super().__init__()
    self.child_attr = 'Child Attribute'
    
  def print(self):
    print("Print from child class - {}".format(self.child_attr))
      
# Create instances for both classes
# and call the print method
parent_obj = ParentClass()
parent_obj.print()
    
child_obj = ChildClass()
child_obj.print()
print()
    
# The the polymorphism behavior, kind of ...
print('Test polymophism')
objects = [parent_obj, child_obj]
for obj in objects:
  obj.print()

与其他面向对象语言一样,Python 支持继承。

  • super() 方法可用于访问父类中的方法;
  • 如果子类实现了相同的方法,则该方法被覆盖;
  • Python 对象表现出与其他语言相似的多态行为。

运行以上程序,我们可以看到以下结果

Print from parent class - Parent Attribute
Print from child class - Child Attribute
    
Test polymophism
Print from parent class - Parent Attribute
Print from child class - Child Attribute

Spark 和 PySpark

关注点

  • 这是一篇关于 Python、Virtualenv、VSC 和其他内容的说明。
  • 希望您喜欢我的帖子,并希望这篇说明能对您有所帮助。

历史

  • 2020年1月28日:初始版本
© . All rights reserved.