面向 .NET 开发者的 Python






4.65/5 (14投票s)
从 .NET 开发者的角度看 Python
引言
我们将从 .NET 开发者的角度探索 Python。根据我的经验,当我们学习任何新的、相似的事物时,我们总是试图找到我们已经知道的东西,并检查在这里如何完成相同的事情。
使用代码
下载 PCC.DataAPI-v1-withNoVE.zip
安装与设置
安装和设置 Python 主要有两种方式。众所周知,微软为我们做了很多事情,所以我们先来看第一种。
a. .NET 的方式
微软旗舰级的 VS2017 提供了内置的 Python 选项。选择安装相同选项并更新设置,它基本上会为你完成所有事情。
b. 世界其他地方
信不信由你,有些开发者并不使用 Visual Studio ;)
- 从 https://pythonlang.cn/downloads/ 下载最新稳定版本并进行安装。
- 安装后,打开命令提示符并输入 pip –version,此命令应该能识别并显示已安装的 Python 版本。
c. MySQL
让我们完全开源,也获取我们将用于 REST API 的 MySQL。
从这里获取安装程序并安装它。
https://dev.mysqlserver.cn/downloads/windows/installer/
并创建一个表并插入一些测试数据。
CREATE TABLE `graphdata` (
`id` int(11) NOT NULL,
`descrption` varchar(45) DEFAULT NULL,
`value` decimal(10,0) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
默认服务器
d. 一些常用包
- 获取 httpie 以从命令提示符调用 REST API。
pip install --upgrade httpie
- 获取 Web 服务器。
pip install waitress
- 连接到 MySQL。
pip install pymysql
快速健全性检查。
- 创建“Hellp.py”文本文件并写入:
- print("Hello Python! I am from .NET world")
- 打开命令提示符。
- 转到文件位置。
- 输入文件名 - “Hellp.py”并回车。
太棒了,它运行了,我可以在控制台中看到结果,此时它引起了我的注意。我的意思是,只有一行代码就能运行,没有 import/using/class 或 function。
好的,这只是控制台,让我们看看它能用 REST API 做什么,现在先放下,继续主要话题。
Visual Studio 中的项目模板
Visual Studio 自带了一些不错的、流行的内置 Python 项目模板。你可以选择其中任何一个开始编写代码,它们自带了所有必需的包。
我尝试了 Django,只是因为它的名字很吸引人,但不太喜欢它。
在 Visual Studio 中安装 Python 包
如果你想在 Visual Studio 中安装任何包,也可以轻松地在 VS 中完成,它自带 IntelliSense(所有拼写错误者的初恋)。
搜索并安装我们将在此使用的 Falcon。
我的选择 - Falcon
我发现(至少感觉)Falcon 简单快捷,你可以在这里找到很好的阅读资料 https://falconframework.org/。
现在我们已经完成了故事铺垫,是时候开始行动了。
a. 解决方案结构
- 让我们创建一个类似这样的解决方案结构(添加同名的文件夹和文件)。
- 在项目中右键单击并转到属性。
- 将 main.py 设置为启动文件。
b. 在你写代码之前
- Python 不使用 { } 来定义作用域,而是使用 TAB,所以要注意缩进和对齐。
- 一个 .py 文件不能同时包含制表符和手动空格。
c. 类创建
让我们创建一个通用类,用于对 JSON 进行十进制编码,令人惊讶的是,Python 的 JSON 包无法处理 decimal(在 json.dumps(..) 中)。
decimalEncoder.py
import falcon
import json
from decimal import Decimal as D
class DecimalEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, D):
return float(obj)
return json.JSONEncoder.default(self, obj)
在此文件中:
- Import = C# 中的 using
- decimal import Decimal as D
- D = C# 中的 using alias
- Decimal 是来自 decimal 文件/包的类。
d. 配置文件
我们有很多配置文件可供选择,我们使用简单方便的 ini 文件。
它是一个简单的键值对文件,有不同的部分。[DEV] 和 [QA]。
Config.ini
[DEV]
DBServer = localhost
User=root
Password=admin
Db=db1
[QA]
DBServer = localhost
User=root
Password=admin
Db=db1
我们将在 secData.py 中使用它。
e. 模块
让我们在 secData.py 中添加代码,这是包含大部分代码的模块。
secData.py
import json
import falcon
import pymysql
import configparser
from Utilities.decimalEncoder import DecimalEncoder
class SecData(object):
def __init__(self,comingFrom):
print("__init__, comingFrom:",comingFrom)
config = configparser.ConfigParser()
config.sections()
config.read("config.ini")
#Global level variable
self.DBServer = config['DEV']['DBServer']
self.Db = config['DEV']['Db']
self.User = config['DEV']['User']
self.Password = config['DEV']['Password']
def getData(self,companyName):
try:
dbConnetion = pymysql.connect(host=self.DBServer,user=self.User,password=self.Password,db=self.Db)
cursor = dbConnetion.cursor()
sql = sql = "SELECT * FROM db1.table1"
if (companyName.lower() != "all"):
sql = "SELECT * FROM db1.table1 where descrption ='" + companyName + "'"
print("sql:",sql)
rowCount = cursor.execute(sql)
rows = cursor.fetchall()
jsonRows = []
for row in rows:
jsonRow = {
'id':row[0]
,'descrption':row[1]
,'value':DecimalEncoder().encode(row[2])}
jsonRows.append(jsonRow)
except OSError as err:
print("OS error: {0}".format(err))
except:
print("Unexpected error:", sys.exc_info()[0])
finally:
cursor.close()
return (jsonRows)
def on_get(self, req, resp,companyName):
print("companyName:",companyName)
print("req.query_string:",req.query_string)
jsonRows = self.getData(companyName)
# Create a JSON representation of the resource
resp.body = json.dumps(jsonRows, ensure_ascii=False)
resp.status = falcon.HTTP_200
现在我们将逐行 walkthrough 代码并解释一些关键点。
f. 哪个代码首先执行?
def __init__(self,comingFrom):
我添加了 comingFrom,只是为了跟踪是谁在实例化它。
这让我想起了 ASP.NET 的页面生命周期。
g. 全局变量
任何使用 self.xyz 的变量。
就像 ASP.NET MVC 中的 ViewBag 一样。
h. 读取配置文件
self.DBServer = config['DEV']['DBServer']
就像从 C# 的 DataTable 获取数据一样。
i. 路由
def on_get(self, req, resp,companyName):
前三个是标准参数;第四个用于从路由获取值。
我们将在 main.py 中使用它。
j. 查询字符串
def on_get(self, req, resp,companyName):
req 包含有关请求的大量信息,目前 req.query_string 会获取查询字符串。
k. 数据库连接
连接数据库非常直接。
dbConnetion = pymysql.connect(host=self.DBServer,user=self.User,password=self.Password,db=self.Db)
is 我们的连接对象,cursor = dbConnetion.cursor() 是数据读取器。
l. JSON 输出
json.dumps(..) 会从对象创建 JSON 并将其分配给 response 对象。
resp.body = json.dumps(jsonRows, ensure_ascii=False)
还将 response 设置为 200 以便一切正常。
resp.status = falcon.HTTP_200
m. 异常处理
就像 C# 一样,Python 也有更具体到更通用类型的异常处理。
try:
# any code
except OSError as err:
print("OS error: {0}".format(err))
except:
print("Unexpected error:", sys.exc_info()[0])
finally:
cursor.close()
n. 代码执行入口点
既然我们已经将 main.py 设置为启动文件,让我们看看里面有什么。
main.py
import falcon
from SecDataModule.secData import SecData
api = application = falcon.API()
secData = SecData("main")
api.add_route('/secdata', secData)
api.add_route('/secdata/{companyName}', secData)
if __name__ == "__main__":
# Use Python's built-in WSGI reference implementation to run
# a web server for the application.
from wsgiref.simple_server import make_server
# Run the web server on localhost:8080
print("Starting web app server")
srv = make_server('localhost', 8080, api)
srv.serve_forever()
- .secData 是在不同文件夹中导入文件的方式。
- '/secdata/{companyName}', secData) 就像 MVC 路由一样。secData 是将处理来自此路由的请求的那个。
o. 启动自己的 Web 服务器
启动 wsgiref 控制台基础服务器。
from wsgiref.simple_server import make_server
srv = make_server('localhost', 8080, api)
srv.serve_forever()
检查一下。
让我们做一些基本测试。
一个简单的请求以 获取所有 数据。
获取 特定 数据。
测试 查询字符串。
https://:8080/secdata/Test1?id=1
在服务器控制台中:
响应
<!--[if !supportLists]--> <!--[endif]--><o:p>
虚拟环境
正如智者所说:*“设置和使用虚拟环境对于 Python 来说是非常标准的做法”*,所以让我们来谈谈添加新的虚拟环境。
顾名思义,虚拟环境允许我们创建环境,在其中可以拥有不同版本的包、库,而不会与全局环境中安装的库发生冲突。
a. 添加虚拟环境
幸运的是 VS 也支持这一点,只需右键单击 Python Environments 并选择 Add Virtual Environment。
命名它,还可以选择更改解释器版本。
b. 快速失败测试
此时代码将无法运行,并应该抛出 falcon ModuleNotFoundError 错误。请记住,我们安装了两个包 Falcon 和 PyMySQL,现在它们在单独的环境中。为了使其正常工作,我们需要将这两个包添加到我们的 v.e. 中。
c. Requirements.txt
这就像 C# 的 packages.config,其中包含所有必需的包,尽管我发现它更简单、更干净。
添加 requirements.txt。
在其中添加 requirements。
==x.x 用于指定包版本,如果未给出版本,则始终安装最新版本。
然后右键单击你的 v.e. 并选择 Install from requirements.txt。
一切就绪,可以开始工作了。
你也可以查看它将包文件转储到哪里。
单元测试
单元测试代码始终是个好主意。我们可以在项目中添加 Python 单元测试文件,并编写类似 C# 的单元测试,然后通过默认的 Test Explorer 运行它。
并编写单元测试以检查数据库连接。
test1.py
import unittest
from SecDataModule.secData import SecData
class Test_test1(unittest.TestCase):
def test_A(self):
jsonRows = []
secData = SecData("FromUnitTest")
jsonRows = secData.getData("Test2")
self.assertIsNotNone(jsonRows)
if __name__ == '__main__':
unittest.main()
运行测试。
TODO
还有很多工作要做,但我在此停下。设置完成,基本代码已运行,轮子正在朝着正确的方向滚动。我相信任何有经验的 .NET 开发者都可以继续下去。
感谢您的阅读,如果您有任何问题,请随时提出(我可以处理 .22LR,但还没有准备好应对马格南弹药 J)。
值得关注的点
参考
Python 官方教程
https://docs.pythonlang.cn/3.7/tutorial/index.html
Falcon
https://falconframework.org
VS 2017 - Python
https://docs.microsoft.com/en-us/visualstudio/python/managing-python-environments-in-visual-studio
历史
在此处保持您所做的任何更改或改进的实时更新。