| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Python for .NET (pythonnet) 是一个包,它使 Python 程序员几乎无缝地集成了 Windows 上的 .NET 4.0+ 公共语言运行时 (CLR) 以及 Linux 和 OSX 上的 Mono 运行时。Python for .NET 为 .NET 开发人员提供了一个强大的应用程序脚本工具。使用这个包,您可以让 .NET 应用程序使用脚本,或者使用 .NET 服务和以 CLR (C#,VB.NET,F#,C++ /CLI) 为目标的任何语言编写的组件通过 Python 构建整个应用程序。
注意,这个包没有将 Python 实现为一流的 CLR 语言——它没有从 Python 代码生成托管代码 (IL)。相反,它将 CPython 引擎与 .NET 或 Mono 运行时集成。这种方法允许您使用 CLR 服务并继续使用现有的 Python 代码和 C-API 扩展,同时维护 Python 代码的本机执行速度。如果您对 Python 语言的纯托管代码实现感兴趣,您应该查看 IronPython 项目,该项目正在积极开发中。
Python for .NET 目前兼容 Python 发行版 2.7, 3.3, 3.4, 3.5 和 3.6。当前版本可以在 Python for .NET 网站上找到。要订阅 Python for .NET 邮件列表或阅读列表的在线档案,查看邮件列表信息页面。使用 Python for .NET 问题跟踪器来报告问题。
Python for .NET 可以作为 GitHub 上的源代码版本使用,也可以作为 Python Package Index 中所有受支持的 Python 版本和公共语言运行时的二进制分发版使用。
源版本是一个自包含的 “私有” 程序集。只需将包解压缩到您想要的任何位置,cd 到该目录,构建解决方案 python setup.py build_ext --inplace。一旦在此目录中启动 Python 或 IPython 解释器或将此目录附加到 sys.path,那么在 import clr 语句之后就可以使用 .NET 程序集了。你也可以运行 nPython.exe (在 *nix 上为 mono nPython.exe) 来查看Python 是如何嵌入到 .NET 控制台应用程序中的。注意,源版本不包含 CPython 运行时的副本,所以在使用源版本之前,您需要在您的机器上安装Python。
在 Linux/Mono 上运行: 单元测试显示 PythonNet 可以在 Mono 下运行,但是 Mono 运行时缺乏支持,所以仍然可能存在问题。
这个项目的一个关键目标是,Python for .NET 应该 “按照你在 Python 中期望的方式工作”,除了特定于 .NET 的情况(在这种情况下,目标是 “按照您在 C# 中期望的方式工作”)。此外,随着IronPython 项目已经建立了一个社区,我的目标是让为 IronPython 编写的代码可以在 Python for .NET 下运行而无需修改。
如果您已经了解 Python,那么您可以完成这篇自述,然后参考 .NET 文档来找出您需要做的任何事情。相反,如果你熟悉 C# 或其他 .NET 语言,那么您可能只需要从许多优秀的 Python 书籍中挑选一本,或者在线阅读 Python 教程就可以开始了。
一种好的开始方法是通过跟随本文档中的示例交互式地探索 .NET 在 Python 解释器中的用法。如果您卡住了,在发行版的源目录中还有许多演示和单元测试,可以作为示例使用。
Python for .NET 使 CLR 命名空间本质上可以被当作 Python 包。
from System import String
from System.Collections import *可以以这种方式导入和使用来自任何加载程序集的类型。要加载程序集,请使用 clr 模块中的 AddReference 函数:
import clr
clr.AddReference("System.Windows.Forms")
from System.Windows.Forms import Form注意 早期版本的 Python for .NET 依赖于 “隐式加载” 来支持程序集的自动加载,这些程序集的名称对应于导入的名称空间。为了向后兼容,隐式加载仍然适用,但是在将来的版本中将被删除,因此建议使用 clr.AddReference 方法。
除了通常的应用程序库和 GAC 之外,Python for .NET 还使用 PYTHONPATH (sys.path) 来查找要加载的程序集。要确保可以隐式导入程序集,请将包含程序集的目录放在 sys.path 中。
Python for .NET 使你可以使用来自 Python 的任何非私有类、结构、接口、枚举或委托。要创建托管类的实例,可以使用标准实例化语法,传递一组匹配托管类的其中一个公共构造函数的参数:
from System.Drawing import Point
p = Point(5, 5)在大多数情况下,Python for .NET 可以根据参数自动确定要调用的正确构造函数。在某些情况下,可能需要在类上调用一个特殊的重载构造函数,该构造函数由一个特殊的 __overloads__ 特性支持,该属性将很快被弃用,转而支持与 iPy 兼容的 "重载":
from System import String, Char, Int32
s = String.Overloads[Char, Int32]('A', 10)
s = String.__overloads[__Char, Int32]('A', 10)Pythonnet 还支持泛型类型。在实例化泛型类型之前,必须绑定泛型类型以创建具体类型。泛型类型支持下标语法来创建绑定类型:
from System.Collections.Generic import Dictionary
from System import *
dict1 = Dictionary[String, String]()
dict2 = Dictionary[String, Int32]()
dict3 = Dictionary[String, Type]()当使用下标语法传递类型列表时,还可以传递与 .NET 类型直接对应的Python 类型子集:
dict1 = Dictionary[str, str]()
dict2 = Dictionary[str, int]()
dict3 = Dictionary[str, Decimal]()当显式选择泛型方法或特定版本的重载方法和构造函数时,这种简写也可以工作 (稍后解释)。
您还可以在 Python 中子类化托管类,尽管 Python 子类的成员对 .NET 代码是不可见的。查看分发版 /demo 目录中的 helloform.py 文件,获取一个演示托管类的子类化的简单 Windows Form 示例。
您可以获取和设置 CLR 对象的字段和属性,就像它们是常规属性一样:
from System import Environment
name = Environment.MachineName
Environment.ExitCode = 1如果托管对象实现了一个或多个索引器,则可以使用标准 Python 索引语法调用索引器:
from System.Collections import Hashtable
table = Hashtable()
table["key 1"] = "value 1"支持重载索引器,使用与 C# 相同的表示法:
items[0, 2]
items[0, 2, 3]CLR 对象方法的行为类似于普通的 Python 方法。静态方法可以通过类调用,也可以通过类的实例调用。Python 可以访问 CLR 对象的所有公共和受保护的方法:
from System import Environment
drives = Environment.GetLogicalDrives()也可以像使用 Python 方法一样调用托管方法 unbound (将实例作为第一个参数传递)。这通常用于显式调用基类的方法。
注意 有一个警告与调用 unbound 方法有关: 托管类可以声明具有相同名称的静态方法和实例方法。由于在调用未绑定方法时,运行时不可能知道你的意图,所以总是会调用静态方法。
可以使用 CLR 方法的 docstring (doc) 查看方法的签名,包括对 CLR 方法时的重载。你还可以使用 Python help 方法来检查托管类:
from System import Environment
print(Environment.GetFolderPath.__doc__)
help(Environment)虽然 Python for .NET 通常能够自动找出要调用的重载方法的正确版本,但是在某些情况下,需要显式地选择特定的方法重载。
CLR 对象的方法有一个可用于此目的的 _overloads__ 特性,但它很快就会被弃用,转而支持与 iPy 兼容的重载:
from System import Console
Console.WriteLine.Overloads[bool](true)
Console.WriteLine.Overloads[str]("true")
Console.WriteLine.__overloads[__int](42)类似地,可以在运行时使用下标语法直接在方法上绑定泛型方法:
someobject.SomeGenericMethod[int](10)
someobject.SomeGenericMethod[str]("10")托管代码中定义的委托可以在 Python 实现。可以实例化委托类型并传递可调用的 Python 对象来获取委托实例。带来的委托实例是一个真正的托管委托,当它被调用时,将调用给定的 Python 可调用对象:
def my_handler(source, args):
print('my_handler called!')
# 实例化一个委托
d = AssemblyLoadEventHandler(my_handler)
# 将它用作事件处理程序
AppDomain.CurrentDomain.AssemblyLoad += d多播委托可以通过向委托实例添加更多可调用对象来实现:
d += self.method1
d += self.method2
d()在 Python 中,事件被视为一类对象,其行为在很多方面类似于方法。Python 回调可以通过事件特性注册,也可以调用事件来触发事件。
注意,事件支持类似于 C# 中使用的方便拼写。您不需要将显式实例化的委托实例传递给事件 (尽管如果需要,可以这样做)。事件支持 += 和 -= 操作符,这与 C# 习惯用法非常相似:
def handler(source, args):
print('my_handler called!')
# 注册事件处理程序
object.SomeEvent += handler
# 注销事件处理程序
object.SomeEvent -= handler
# 触发事件
result = object.SomeEvent(...)你可以像处理纯 Python 异常一样引发和捕获托管异常:
from System import NullReferenceException
try:
raise NullReferenceException("aiieee!")
except NullReferenceException as e:
print(e.Message)
print(e.Source)类型 System.Array 支持下标语法,以便在 Python 中轻松创建托管数组:
from System import Array
myarray = Array[int](10)托管数组支持标准 Python 序列协议:
items = SomeObject.GetArray()
# 获取第一项
v = items[0]
items[0] = v
# 获取最后一项
v = items[-1]
items[-1] = v
# 获取长度
l = len(items)
# 内容测试
test = v in items多维数组支持使用与 C# 中使用的表示法相同的方法进行索引:
items[0, 2]
items[0, 2, 3]实现 IEnumerable 接口的托管数组和托管对象可以使用标准的 Python 迭代习惯用法进行迭代:
domain = System.AppDomain.CurrentDomain
for item in domain.GetAssemblies():
name = item.GetName()使用微软提供的工具,如 aximp.exe 和 tlbimp.exe,可以为 COM 库生成托管包装器。在生成这样一个包装器之后,您可以像使用其他托管代码一样在 Python 中使用这些库。
注意: 目前,您需要将生成的包装器放在 GAC、PythonNet 程序集目录或 PYTHONPATH 中,以便加载它们。
在 Python for .NET 下进行的类型转换相当简单——大多数基本的 Python 类型 (string、int、long等) 自动转换为兼容的对等托管类型 (string、Int32 等),反之亦然。注意,从 CLR 返回的所有字符串都是 unicode 类型。
在 Python 中没有逻辑等同物的类型被公开为托管类或结构体 (System.Decimal 就是一个例子)的实例。
.NET 架构区分了 值类型 和 引用类型。引用类型在堆上分配,值类型要么在堆栈上分配,要么在对象中内联分配。
在 .NET 中使用了一个名为 装箱 的过程,允许代码将值类型当作引用类型。装箱导致在堆上创建值类型对象的单独副本,这样就会具有引用类型语义。
在 Python for .NET 时,理解装箱以及值类型和引用类型之间的区别可能非常重要,因为 Python 语言没有值类型语义或语法——在 Python 中 “一切都是引用”。
下面是一个演示问题的简单示例。如果你是一个经验丰富的 C# 程序员,你可能会编写以下代码:
items = System.Array.CreateInstance(Point, 3)
for i in range(3):
items[i] = Point(0, 0)
items[0].X = 1 # won't work!!虽然 items[0].X = 1 的拼写在 C# 和 Python 中是相同的,但还是有一个重要的和微妙的语义差异。在 C# (和其他编译到 IL 的语言) 中,编译器知道 Point 是值类型,在这里可以做正确的事情,那就是在适当的位置更改值。
然而,在 Python 中,“一切都是引用”,实际上没有拼写或语义来允许它动态地做正确的事情。items[0] 本身没有变化的具体原因是,当你说到 items[0] 时,getitem 操作创建了一个 Python 对象,该对象通过 GCHandle 保存对 items[0] 对象的引用。这会导致 ValueType (类似 Point) 被装箱,因此接下来的 setattr (.X = 1) 更改已装箱值的状态,而不是原始未装箱值的状态。
Python 中的规则实质上是:
任何属性或项访问的结果都是一个已装箱的值
而这对于处理代码很重要。
因为 Python 中没有值类型语义或语法,所以您可能需要修改您的方法。为了重温前面的例子,我们可以确保我们想要对数组项进行的更改不会 “丢失”,方法是在对数组项进行更改后重置数组成员:
items = System.Array.CreateInstance(Point, 3)
for i in range(3):
items[i] = Point(0, 0)
# 这 _将会_ 工作。我们将 “item” 作为 Point 对象的
# 装箱副本实际存储在数组中。进行更改后,我们重新
# 设置数组项以更新数组。
item = items[0]
item.X = 1
items[0] = item这与你在 C# 中发现的一些情况类似,您必须了解装箱行为,以避免类似 更新丢失 的问题 (通常是因为代码中没有考虑到隐式装箱)。
这是一样的,只是在 Python 中表现有点不同。有关装箱以及值类型和引用类型之间的区别的更多细节,请参阅 .NET 文档。
注意: 因为在 Python for .NET 中运行的 Python 代码本质上是不可验证的,它完全在 CLR 的安全设施监视下运行,因此您应该将对 Python 程序集的使用限制为受信任的代码。
Python 运行时程序集定义了许多公共类,它们提供 Python C-API 所提供功能的子集。
这些包括 PyObject, PyList, PyDict, PyTuple, 等等。你可以在 "Console.csproj" 项目中回顾 nPython.exe 源代码,它是在 .NET 控制台应用中嵌入 CPython 的例子。有关新的简化嵌入 API,请参阅 GitHub README 页面:
在非常高的级别上,要将 Python 嵌入到应用程序中,您需要这样做:
导入的模块可以在导入时就开始与托管应用程序环境一起工作,否则的话,你也可以显式地查找和调用导入模块中的对象。
有关在应用程序中嵌入 Python 的通用信息,请使用 www.python.org 或谷歌查找(C)示例。因为 Python for .NET 与托管环境紧密集成,所以通常最好导入模块并尽早遵从Python代码,而不是编写大量托管嵌入代码。
对嵌入用户的重要提示: Python 不是线程自由的,它使用一个全局解释器锁来确保多线程应用程序与 Python 安全交互。有关这方面的更多信息可以在 www.python.org 网站的 Python C-API 文档中找到。
在托管应用程序中嵌入 Python 时,必须像在 C 或 C++ 应用程序中嵌入 Python 那样管理 GIL。
在与 Python.Runtime 命名空间提供的任何对象或 api 交互之前,调用代码必须通过调用 PythonEngine.AcquireLock 方法获得 Python 全局解释器锁。这条规则的唯一例外是 PythonEngine.Initialize 方法,它可以在启动时调用,而不需要获取 GIL。
使用完 Python API 后,托管代码必须调用对应的 PythonEngine.ReleaseLock 来释放 GIL 并允许其他线程使用 Python。
必须使用 using 语句来获取和释放 GIL:
using (Py.GIL())
{
PythonEngine.Exec("doStuff()");
}AcquireLock 和 ReleaseLock 方法是对 Python API 中的非托管函数 PyGILState_Ensure 和 PyGILState_Release 的简单封装,所以这些 API 的文档适用于托管版本。
本节演示如何将 C# 对象传递给 Python 运行时。示例使用下面的 Person 类:
public class Person
{
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public string FirstName { get; set; }
public string LastName { get; set; }
}为了将 C# 对象传递给 Python 运行时,必须将其转换为 PyObject。这是使用 ToPython() 扩展方法完成的。然后可以将 PyObject 设置为 PyScope 中的一个变量。从作用域执行的代码将有权访问这个变量:
// 创建一个 person 对象
Person person = new Person("John", "Smith");
// 在使用 Python 解释器之前获取 GIL
using (Py.GIL())
{
// 创建 Python 作用域
using (PyScope scope = Py.CreateScope())
{
// 将 Person 对象转换为 PyObject
PyObject pyPerson = person.ToPython();
// 创建 Python 变量 "person"
scope.Set("person", pyPerson);
// person 对象现在可以在 Python 中使用了
string code = "fullName = person.FirstName + ' ' + person.LastName";
scope.Exec(code);
}
}Python for .NET 基于开源 MIT 许可证发布。许可证的副本包含在发行版中,或者你也可以找到 license online 的副本。
这个包的一些发行版包括 C Python 动态库和标准库的副本,它们由 Python license 许可。
| Back | FazBrowse Home | New Git URL |