Mojo 学习 —— 与 Python 交互

Mojo 学习 —— 与 Python 交互


为了使 Mojo 成为 Python 的超集,必须保证能与现有的 Python 程序兼容。 Python 使用者应能很快入手 Mojo,并能够使用 Python 软件包生态系统。

但是,Mojo 仍处于早期开发阶段,许多功能尚未实现。目前还不能在 Mojo 中编写在 Python 代码。也还没有搭建好自己的软件包生态系统。

作为一个替代方案,Mojo 允许您导入 Python 模块,并调用 Python 函数,与 Python 对象交互。

Mojo 使用标准解释器(CPython)运行 Python 代码,可与现有的 Python 代码完美兼容。

与 Python 集成

导入 Python 模块

使用 python 模块中的 Python.import_module 可以导入 Python 模块。例如:

from python import Python

fn use_array() raises:
    # 相当于 import numpy as np
    var np = Python.import_module("numpy")

    # 使用 numpy
    var array = np.array([1, 2, 3])
    print(array)
    
use_array()

您也可以导入已安装的任何其他 Python 模块。

但是有几点需要注意:

  • 目前还不支持单个成员(如单个类或函数)的导入,必须导入整个模块
  • 由于 Mojo 尚不支持顶层代码,因此 import_module 必须在函数内部调用。多次导入模块不会多次运行初始化逻辑,不会对性能造成任何影响。
  • import_module 函数可能会引发异常(例如,模块未安装)。需要在函数中处理异常(使用 try/except 子句,或者在函数签名中添加 raises 关键字)。

Mojo 会在运行时加载 Python 解释器及 Python 模块,所以它必须能够访问兼容的 Python 解释器,并找到任何导入的 Python 模块。

导入本地 Python 模块

如果您想在 Mojo 中使用本地 Python 脚本,只要将脚本所在路径添加到 Python 搜索路径即可。

例如,假设您在 path/to/module 有一个名为 mypython.py 的文件:

import numpy as np

def gen_random_values(size, base):
    random_array = np.random.rand(size, size)
    return random_array + base

Mojo 文件中导入并使用:

from python import Python

fn main() raises:
    Python.add_to_path("path/to/module")
    var mypython = Python.import_module("mypython")

    var values = mypython.gen_random_values(2, 3)
    print(values)

add_to_path 支持绝对路径和相对路径。例如,添加当前路径:

Python.add_to_path(".")

Python 环境

Mojo SDK 依赖于 Python 环境。当您安装 Mojo SDK 时,它会尝试查找兼容版本的 Python 解释器,并设置 Pythonsys.path 以加载匹配的模块。

在大多数情况下,这都能正常工作,但有时候也会出现一些安装问题。

安装问题

安装程序运行时,会尝试使用 find_libpython 模块查找 CPython 的共享库。

如果包含以下情况,则可能会失败:

  • 没有安装 Python,或者安装的版本与 Mojo SDK 不匹配。
  • 安装程序找不到 CPython 解释器的共享库(例如 .so.dylib 文件)。有些 Python 发行版不包含共享库,导致 Mojo 无法嵌入解释器。

最好的方法是使用 conda 重新配置 Python 版本及环境

用 Conda 配置 Python 环境

可以查看前面介绍的环境配置章节

简单来说就是

  1. 快速安装 conda(使用 miniconda
mkdir -p ~/miniconda3
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O ~/miniconda3/miniconda.sh
bash ~/miniconda3/miniconda.sh -b -u -p ~/miniconda3
rm -rf ~/miniconda3/miniconda.sh
  1. 为你的一个或多个 shell 初始化 conda
~/miniconda3/bin/conda init zsh

或者

~/miniconda3/bin/conda init --all
  1. 重启 shell
  2. 配置 Mojo 使用 conda 环境中的 Python 共享库:
export MOJO_PYTHON_LIBRARY="$(find $CONDA_PREFIX/lib -iname 'libpython*.[s,d]*' | sort -r | head -n 1)"
echo "export MOJO_PYTHON_LIBRARY=$MOJO_PYTHON_LIBRARY" >> ~/.zshrc

如果使用的 shell 不是 zsh,例如 bash,则需要将 .zshrc 替换为你使用的 shell 配置文件,如 .bashrc.bash_profile

  1. 安装任何要与 Mojo 一起使用的 Python 软件包。例如
conda install numpy
  1. 重新测试上面的代码
from python import Python

fn use_array() raises:
    # 相当于 import numpy as np
    var np = Python.import_module("numpy")

    # 使用 numpy
    var array = np.array([1, 2, 3])
    print(array)
    
use_array()

类型交互

当调用 Python 方法时,Mojo 会自动在 Python 对象和 Mojo 对象之间来回转换。但也存在一些 Mojo 无法处理的情况,您可能需要进行显式转换,或者调用一个额外的方法。

Python 使用 Mojo 类型

Mojo 原始类型如列表、元组、整数、浮点数、布尔和字符串,可以隐式转换为 Python 对象。

例如,在 jupyter notebook 中,定义一个 Python 函数:

%%python
def type_printer(value):
    print(type(value))

用该函数打印 Mojo 类型:

type_printer(4)
type_printer(3.14)
type_printer(("Mojo", True))
type_printer(['A', "b", """c"""])
# <class 'int'>
# <class 'float'>
# <class 'tuple'>
# <class 'list'>

自动转换为了 Python 对象

jupyter notebook%%python 表示该单元中的代码为 Python 代码,。第二个单元包含顶级 Mojo 代码。

Mojo 使用 Python 类型

您还可以在 Mojo 中使用 Python 对象。例如,使用 Python 字典

from python import Python

fn use_dict() raises:
    var dictionary = Python.dict()
    dictionary["fruit"] = "apple"
    dictionary["starch"] = "potato"
    print("Fruit: ", dictionary["fruit"])
Mojo 封装对象

Mojo 中使用 Python 对象时,Mojo 会将其封装为 PythonObject 类型。

该对象包含许多常用的双下划线方法(魔法方法),如 __getitem____getattr__,并将它们传递给底层 Python 对象。

我们可以使用 PythonObject 来显式地创建一个封装的 Python 对象:

from python import PythonObject

var py_list: PythonObject = [1, 2, 3, 4]

Mojo 字面量会转换为 PythonObject 类型,我们可以像 Python 对象一样使用它。例如

var n = py_list[2]
py_list.append(5)

如果 Mojo 中没有对应的字面形式,可以使用 Python.evaluate 方法,调用 Python 代码来创建。例如:

from python import Python

fn use_py_set() raises:
    var py_set = Python.evaluate('set([2, 3, 5, 7, 11])')
    var num_items = len(py_set)
    print(num_items, " items in set.")  # prints "5 items in set"
    print(py_set.__contains__(6))

use_py_set()

PythonObject 当前的工作方式还不支持使用 in 来做成员判断,需要直接调用 __contains__ 方法

目前,PythonObject 符合 IntableStringable 特性,所以可以使用内置的 intstr 函数将 Python 值转换为 MojoIntString 类型,并使用内置的 print 函数打印 Python 值。

PythonObject 还提供了 __bool__to_float64方法,分别用于转换为布尔值和浮点数。

var i: Int = int(py_int)
var s: String = str(py_string)
var b: Bool = py_bool.__bool__()
var f: Float64 = py_float.to_float64()

我们前面说的 Python 类型会被封装为 PythonObject 类型对象。但有一个除外,Python 字典被封装成了 Dictionary,但它并不是真正的字典类型,而是另一种封装形式。

比较 Python 类型

我们可以在条件表达式中使用 Python 对象,使用方式与 Python 类似。可以使用 Python.type 方法来获取其对应的 Python 类型,等同于 Python 的内置函数 type

您可以使用 Python.is_type 方法(相当于 Pythonis 操作符)比较两个 Python 对象的标识,例如

fn python_types() raises:
    from python import Python
    from python import PythonObject

    var value1: PythonObject = 3.7
    var value2 = Python.evaluate("10/3")
    var float_type = Python.evaluate("float")

    if value1 > value2:
        print(value1, '>', value2)  # 3.7 > 3.3333333333333335
    else:
        print(value1, '<', value2)
    print(Python.type(value1))      # <class 'float'>
    print(Python.is_type(Python.type(value1), Python.type(value2)))  # True
    print(Python.is_type(Python.type(value1), float_type))           # True
    print(Python.is_type(Python.type(value1), Python.none()))        # False

python_types()

Python.is_type 方法的名称有误导性,因为它不是比较类型,而是比较对象身份,后续版本将会进行调整

简单示例

我们编写一个稍微复杂一点的例子,更贴合目前 Mojo 的使用方式。从 KEGG 中获取 TP53 基因所涉及到的通路信息。具体思路为:

  1. 首先,定义一个结构体(Pathway)来存储通路信息
  2. 然后,使用 requests 模块获取网页信息(使用 KEGG API
  3. 使用 re 模块的正则表达式来进行字符串提取
  4. 最后将提取的通路信息存放到结构体列表中
from collections import List
from python import Python

@value
struct Pathway:
    var id: String
    var name: String

    fn __str__(self) -> String:
        return '(ID=' + self.id + '; ' + 'Name=' + self.name + ')'

def get_url(url: String) -> List[Pathway]:
    var res = List[Pathway]()
    try:
        var requests = Python.import_module('requests')
        var re = Python.import_module('re')
        req = requests.get(url)

        content = str(req.text)
        var label: String = ''
        for line in content.split('\n'):
            if line[][0] != ' ': label = line[][:7]
            if label == 'PATHWAY':
                var col = re.findall('(hsa\\d+)\\s+(.+)', line[])[0]
                var path = Pathway(col[0], col[1])
                res.append(path^)
    except e:
        print(e)
    return res

fn main() raises:
    var url = "https://rest.kegg.jp/get/hsa:7157"
    var res = get_url(url)
    print('Pathway List: ')
    for p in res[:6]:
        print(p[])

输出结果

Pathway List: 
(ID=hsa01522; Name=Endocrine resistance)
(ID=hsa01524; Name=Platinum drug resistance)
(ID=hsa04010; Name=MAPK signaling pathway)
(ID=hsa04071; Name=Sphingolipid signaling pathway)
(ID=hsa04110; Name=Cell cycle)
(ID=hsa04115; Name=p53 signaling pathway)
  • 21
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

名本无名

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值