文章
· 七月 12, 2022 阅读大约需 5 分钟

第三章 嵌入式Python概述(三)

第三章 嵌入式Python概述(三)

使用 SQL

IRIS 中的类被投影到 SQL,除了使用类方法或直接全局访问之外,还允许使用查询访问数据。 iris 模块为提供了两种从 Python 运行 SQL 语句的不同方式。

以下示例使用 iris.sql.exec() 运行 SQL SELECT 语句以查找类名称以“%Net.LDAP”开头的所有类定义,返回一个包含每个名称和超类的结果集每个班级。在这里,系统类 %Dictionary.ClassDefinitionSQL 投影为同名表。

>>> rs = iris.sql.exec("SELECT Name, Super FROM %Dictionary.ClassDefinition WHERE Name %STARTSWITH '%Net.LDAP'")

以下示例使用 iris.sql.prepare() 准备 SQL 查询对象,然后执行查询,将“%Net.LDAP”作为参数传入:

>>> stmt = iris.sql.prepare("SELECT Name, Super FROM %Dictionary.ClassDefinition WHERE Name %STARTSWITH ?")
>>> rs = stmt.execute("%Net.LDAP")

无论哪种情况,都可以按如下方式遍历结果集,并且输出相同:

>>> for idx, row in enumerate(rs):                                              
...     print(f"[{idx}]: {row}")                                                
...
[0]: ['%Net.LDAP.Client.EditEntry', '%RegisteredObject']
[1]: ['%Net.LDAP.Client.Entries', '%RegisteredObject,%Collection.AbstractIterator']
[2]: ['%Net.LDAP.Client.Entry', '%RegisteredObject,%Collection.AbstractIterator']
[3]: ['%Net.LDAP.Client.PropList', '%RegisteredObject']
[4]: ['%Net.LDAP.Client.Search.Scope', '%Integer']
[5]: ['%Net.LDAP.Client.Session', '%RegisteredObject']
[6]: ['%Net.LDAP.Client.StringList', '%RegisteredObject']
[7]: ['%Net.LDAP.Client.ValueList', '%RegisteredObject,%Collection.AbstractIterator']

使用Globals

IRIS 数据库中,所有数据都存储在全局变量中。全局数组是持久的(意味着它们存储在磁盘上)、多维的(意味着它们可以有任意数量的下标)和稀疏的(意味着下标不必是连续的)。当您在表中存储类的对象或行时,这些数据实际上存储在全局变量中,尽管您通常通过方法或 SQL 访问它们并且从不直接接触全局变量。

有时将持久数据存储在全局变量中会很有用,而无需设置类或 SQL 表。在 IRIS 中,全局变量看起来很像任何其他变量,但它在名称前用插入符号 (^) 表示。以下示例将工作日的名称存储在当前命名空间的全局 ^Workdays 中。

>>> myGref = iris.gref('^Workdays')
>>> myGref[None] = 5
>>> myGref[1] = 'Monday'
>>> myGref[2] = 'Tuesday'
>>> myGref[3] = 'Wednesday'
>>> myGref[4] = 'Thursday'
>>> myGref[5] = 'Friday'
>>> print(myGref[3])
Wednesday

第一行代码 mmyGref = iris.gref('^Workdays') 获取一个全局引用(或 gref),指向一个名为 ^Workdays 的全局引用,它可能已经存在也可能不存在。

第二行 myGref[None] = 5 将工作日数存储在 ^Workdays 中,不带下标。

第三行 myGref[1] = 'Monday' 将字符串 Monday 存储在位置 ^Workdays(1) 中。接下来的四行将剩余的工作日存储在位置 ^Workdays(2)^Workdays(5) 中。

最后一行 print(myGref[3]) 显示了如何在给定 gref 的情况下访问存储在全局中的值。

一起使用 ObjectScript 和 Python

IRISObjectScriptPython 程序员的混合团队轻松协作。例如,类中的一些方法可以用 ObjectScript 编写,而另一些可以用 Python 编写。程序员可以选择用他们最熟悉的语言编写,或者更适合手头任务的语言。

创建混合 InterSystems IRIS 类

下面的类有一个用 Python 编写的 Print() 方法和一个用 ObjectScript 编写的 Write() 方法,但它们在功能上是等效的,并且可以从 PythonObjectScript 调用这两种方法。

Class Sample.Company Extends (%Persistent, %Populate, %XML.Adaptor)
{

/// The company's name.
Property Name As %String(MAXLEN = 80, POPSPEC = "Company()") [ Required ];

/// The company's mission statement.
Property Mission As %String(MAXLEN = 200, POPSPEC = "Mission()");

/// The unique Tax ID number for the company.
Property TaxID As %String [ Required ];

/// The last reported revenue for the company.
Property Revenue As %Integer;

/// The Employee objects associated with this Company.
Relationship Employees As Employee [ Cardinality = many, Inverse = Company ];

Method Print() [ Language = python ]
{
    print ('\nName: ' + self.Name + ' TaxID: ' + self.TaxID)
}

Method Write() [ Language = objectscript ]
{
    write !, "Name: ", ..Name, " TaxID: ", ..TaxID
}
}

Python 代码示例展示了如何使用 %Id=2 打开 Company 对象并调用 Print()Write() 方法。

>>> company = iris.cls("Sample.Company")._OpenId(2)
>>> company.Print()

Name: IntraData Group Ltd. TaxID: G468
>>> company.Write()

Name: IntraData Group Ltd. TaxID: G468

ObjectScript 代码示例展示了如何打开相同的 Company 对象并调用这两种方法。

SAMPLES>set company = ##class(Sample.Company).%OpenId(2)

SAMPLES>do company.Print()

Name: IntraData Group Ltd. TaxID: G468

SAMPLES>do company.Write()

Name: IntraData Group Ltd. TaxID: G468

在 Python 和 ObjectScript 之间传递数据

虽然 PythonObjectScript 在许多方面都兼容,但它们有许多自己的数据类型和结构,有时在将数据从一种语言传递到另一种语言时需要进行一些数据转换。之前看到了一个示例,即从 ObjectScriptPython 传递命名参数的示例。

%SYS.Python 类的 Builtins() 方法为提供了一种方便的方式来访问 Python 的内置函数,它可以帮助创建 Python 方法所期望的类型的对象。

以下 ObjectScript 示例创建两个 Python 数组 newportcleveland,每个数组都包含一个城市的纬度和经度:

USER>set builtins = ##class(%SYS.Python).Builtins()

USER>set newport = builtins.list()

USER>do newport.append(41.49008)

USER>do newport.append(-71.312796)

USER>set cleveland = builtins.list()

USER>do cleveland.append(41.499498)

USER>do cleveland.append(-81.695391)

USER>zwrite newport
newport=11@%SYS.Python  ; [41.49008, -71.312796]  ; <OREF>

USER>zwrite cleveland
cleveland=11@%SYS.Python  ; [41.499498, -81.695391]  ; <OREF>

下面的代码使用在前面的示例中看到的 geopy 包来计算纽波特,罗德岛和克利夫兰,俄亥俄州之间的距离。它使用 geopy.distance.distance() 方法创建一条路线,将数组作为参数传递,然后打印路线的英里属性。

USER>set distance = $system.Python.Import("geopy.distance")

USER>set route = distance.distance(newport, cleveland)

USER>write route.miles
538.3904453677205311

注意: geopy.distance.distance() 方法实际上期望参数是 Python 元组数据类型,但数组也可以。

运行 Python 命令

当开发或测试某些东西时,有时运行一行 Python 代码以查看它的作用或是否有效可能会很有用。在这种情况下,可以使用 %SYS.Python.Run() 方法,如下例所示:

USER>set rslt = ##class(%SYS.Python).Run("print('hello world')")
hello world
讨论 (1)1
登录或注册以继续