写给Python用户的Julia编程指南[updating]

本文的目的是,为拥有Python编程经验的用户提供一些指引,方便其快速上手Julia。

准备

下载和安装

官网提供了各个系统下的安装包,不过我建议新手先安装Julia PRO,这个有点类似Python下的Anaconda,目前版本是0.6.2,安装Julia的同时还会安装一些Julia下常见的包(省去了许多包管理麻烦,当然,也会带来一些新的小问题,这个碰到的时候再细说)。

由于我日常开发在Windows下,所以以下内容中操作系统相关的部分都以Windows为主(Linux和Mac下要容易很多,应该问题不大)。

安装后直接双击桌面的JuliaPro - Command Prompt 0.6.2.1快捷键即可打开Julia的REPL界面,就跟IPython有点像(后面会详细介绍其与IPython的联系和区别),建议将JuliaPRO安装路径下Julia-0.6.2中的bin目录添加到环境变量Path中,这样可以直接在git-bash或者cmd中直接运行julia(如下图)。

Julia_REPL

Julia_REPL

编辑器

一般Python的开发会选择PyCharm之类的IDE,不过Julia下还没有与PyCharm对应的IDE。安装JuliaPRO之后,桌面会有一个Juno for JuliaPro 0.6.2.1,其实就是一个Atom上套了个插件,如果你原来就用Atom的话,也许你会喜欢这个编辑环境。我个人在Windows下倾向使用VSCode+Julia插件,不过社区里也有人用Vim和Emacs,选择一个适合你的就好。

使用VSCode的Julia插件的时候,需要配置(File -> Preferences -> Settings)julia的路径,如"julia.executablePath": "D:\workspace\software\JuliaPro-0.6.2.1\Julia-0.6.2\bin\julia.exe"。

当然,你一定注意到了桌面还有一个Jupyter 0.6.2.1,其实就是Jupyter+IJulia的内核,如果你习惯Jupyter的话几乎可以做到无缝迁移。

REPL

在IPython中,最常用的一个功能是查看函数的帮助文档,通过在函数/方法名后面加一个?来实现,不过在Julia的REPL中,是先输入?再输入函数名:

使用?的例子

使用?的例子

另外,在IPython中,经常会执行一些类似ls,pwd等等系统命令,这类命令在Julia中是通过函数来实现的(如readdir(), pwd())。此外,原来%%的魔法函数也有对应的函数实现。

包管理

在Python中,我们一般通过pip这个工具在命令行里管理Python的包,在Julia中,包管理的工具是Pkg,其集成在了Julia中,因此,所有包管理相关的操作都在REPL中执行即可,几个常用的命令为:

  1. Pkg.status,查看当前已安装包的版本
  2. Pkg.add,添加某个包
  3. Pkg.build,编译某个包(某些包需要编译后才能运行)
  4. Pkg.test,执行test

你可以通过在REPL中输入Pkg.然后按TAB键查看Pkg下都还有哪些其它方法,然后用前面提到的方式查看对应的文档。

Pkg使用的例子

Pkg使用的例子

Pkg目前是采用git在管理,有好处也有坏处,社区里提出了Pkg3,可能会缓解一些奇奇怪怪的问题。

Julia社区发展很快,许多包都在频繁更新中,有时候我们希望使用某个包的最新功能,但是该版本可能并不满足JuliaPRO的requirement,这时候可以通过Pkg.checkout("package_name")来拉取最新的版本。但是需要慎重,因为可能其它依赖它包并不支持该最新版本。此时有两个选择,要么妥协,先使用旧版本,要么不再依赖JuliaPRO的包依赖管理,在一个新的环境里只安装自己需要的包,然后执行Pkg.resolve()尝试解决冲突。当然,最理想的情况是,没有其它包依赖该包,然后只会看到一个Warning。

Pkg.checkout的一个例子

Pkg.checkout的一个例子

语法细节

下面从一些日常的操作来介绍如何从Python迁移到Julia。

代码结构

在Python中,代码是以目录结构组织起来的,然后用__init__.py来区分包和普通的目录,Julia的代码以模块(Modules)组织起来的,一个典型的Julia文件有如下结构:

module MyModule
using Lib

using BigLib: thing1, thing2

import Base.show

importall OtherLib

export MyType, foo

struct MyType
    x
end

bar(x) = 2x
foo(a::MyType) = bar(a.x) + 1

show(io::IO, a::MyType) = print(io, "MyType $(a.x)")
end

首先,module定义了整个模块,其作用范围一直到最后一行end,module名称一般采用驼峰式,在module中也可以定义一个__init__函数,其功能有点类似Python下的__init__.py文件(不完全对应,__init__.py某种程度上提供了下面export的功能)。

using Lib有点类似Python中的from xxx import *,此刻你脑海中想到的第一个问题应该是命名冲突!对,我一开始也这么想的,后来习惯了发现,好像问题不大,因为Julia是带类型的,一定程度上通过重载缓解了这个问题。

using BigLib: thing1, thing2就是显式地导入,类似from xxx import foo, bar

个人感觉,Julia中import主要用于扩展某个函数的时候使用,这一点using做不到(using只用于提供查找变量的搜索空间)。importall就是import的扩展版。

export则是,将该module内部的某些变量暴露出去。在Python中可能是通过__foo等来实现的。

函数部分的区别和联系后面再详述。

有时候,我们想直接从某个文件中导入,需要用include("foo.jl")来实现。

有一个函数whos()可以用来查看当前的变量信息,有点像Python中的globals()

数据结构

index

在Julia中,按index访问访问元素的时候,是从1而不是0开始的。这大概是我自己从Python迁移到Julia最不习惯的一方面(一不小心就出错了)。访问最后一个元素要使用end关键字,有点像Python中的-1。另外,Julia中索引的时候,是左闭右闭[]的,不是Python中的左闭右开[)

string

list

Python中最常用的就是list了,Julia中并没有严格与之对应的数据结构,不过,就一般使用而言,可以把一维的Array{Any,1}拿来用。

Tuple

和Python中基本一致,未来也会支持类似Python3中的NamedTuple。

numpy

这里单独说明下,Julia可以看作是自带了numpy的Python,Numpy的许多操作都能在标准库中Array部分找到。

iterator

struct

对比Python中的class

macro

这个先放这里,在Python中很少会接触,前期迁移到Julia中的时候,只需要掌握一些常用的宏就可以了。

Grammar

Julia中有些Python中没有的语法糖,个人感觉有些还算有意思,有些就比较累赘了。

function

如果不考虑类型的话,Julia下的function和Python相比,主要的差别是:

operator

接下来?