类是编程语言基本抽象概念,比如实数、整数、字符串等等。一般分成静态和动态的,如果代码根据不同的类可以执行不同的操作,称为Polymorphism(多态的),动态语言一般都是是多态的。 Julia是动态类语言,不过也继承了静态类的高效性,如果不添加类型声明,则值是任意类,如果指定类,可以显著提高计算效率和稳定性。 Julia类型的特点也成为它高效的一个重要原因。Julia的类型系统不支持类似于Python/Cpp那样以对象的成员隐式继承,类之间的继承关系需要明确指定(据说继承了lisp的元编程特点,不过我不懂lisp)。

Julia中的所有实体类都是最终类,它们的父类只可能是抽象类。julia中的类都是面向对象的,只有执行类(run-time type)不存在编译类(compile-time type),只有value含有类型,变量只是值的名字。

Julia 对于不指定类执行默认类型,默认类型是任意类,这样我们就可以非常方便的定义函数。让绝大多数程序可以体会不到类的存在。

     f(x,y)=x^2+y^2

后续也可以对默认类型的函数追加声明类型,这样有三个好处:

  1. julia有极为强大的多重派发功能
  2. 增加可读性
  3. 便于发现程序错误

类的声明

类通过::进行声明,可以读作“is an instance of”

 function test()x::Int8 = 32endtest()  #32typeof(ans) #Int8
 function sinc()::Float64if x==0return 1endreturn  in(pi*x)/(pi*x)end

可以将结果转化为Float64。

类的断言

 function sinc()::Float64if x==0return 1endreturn  in(pi*x)/(pi*x)endsinc(0)::Int8 #TypeError: typeassert: expected Int8, got Float64

或者用

 a=8isa(a,Int) #True

丰富的原始类型(位类型)

julia提供丰富的位类型体现专业的计算能力,如Int8 Int16 Int32 Int64 Int128,UInt8… Uint128,Float8…Float128

抽象类

 Int8  <: Signed # true

其中<: is a subtype of 用来确定是否子类。

组合类

组合类在不同语言中叫法不同,可能称为“records、structs、objects“等,在很多语言中组合类是唯一用户自定义类,在julia中也是最主要的用户自定义类型,在主流面向对象语言中,如c++、java、python中,function是和组合类耦和在一起的,构成“Objects”,在更纯粹的面相对象语言中,如ruby 和smalltalks,无论是不是组合类,值都是对象;在不是那么纯粹的面向对象语言中,如c++ 和Java中,integer 和浮点值不是对象,而用户定义的类是和方法一起构成对象的。在julia里,所有值都是都是对象,但是function并不和他们作用的对象绑定在一起。这也是多重派发的基础。

组合类通过struct开辟,

 struct Mystructbaxbay::Int8baz::Float64end

通过类似于函数的方式可以组合类 的值

   foo = Mystruct("Hello", 64, 3.2)typeof(foo) #Mystruct

可以看组合类的列表

     fieldnames(Mystruct) # :bax, :bay, :baz

调用组合类

    foo.bax #"Hello"foo.bay #64

我们可以测试一下,再开辟一个值

 foo2 = Mystruct(66, 3, 5.5)foo2.bax # 66foo2.bay # 3foo2.baz # 5.5

可见对于未定义类bax,foo和foo2并不冲突。
Julia 的对象一经声明后,默认是不可更改的(immutable)。

可以通过

 mutable struct Barbaxbay::Float64end bar = Bar("Hello", 3.5)bar.bay = 1//12

可以更改类。

总结

Julia不变类具有两个基本特点:

  1. 不变类不可更改
  2. 不变类具有免复制的特性,因为编译器并无法区分它本身和它的复本。

断言类型

上面说到的三种类型:抽象类,原始类,组合类,实际上是非常相关的,比如都是显式断言,含有名字,含有显式断言的父类,可能含有参数。 因此他们遵循相同的逻辑,这些类都属于同一类 DateType

  typeof(Real) #DateTypetypeof(Int)  #DateType

类的结合

通过关键字 Union 可以将不同的类结合

  IntOrString = Union{Int, AbstractString}  #Union{ Int, AbstractString}"what" :: IntOrString  #"what"1.0 :: IntOrString  #Error

类的参数化

对于固定类,如果想调节类型很困难,julia中提供了一个重要特性,就是参数化,所有声明类型(DataType)都可以参数化。

组合类参数化

 struct Point{T}arg::Tlin::Tend

这样做的好处,我们可以通过Point分别开辟不同的固定组合类

 point = Point(2.3,4.5) # Point{Float64}(2.3,4.5)point2 = Point{Int16}{3,4)

Point同样是一个有类型的对象,含有所有参数子类。

 Point{Float64} <: Point # True

抽象类参数化

抽象类参数化是相似的

abstract type Pointy{T} end
Pointy{Float64} <: Pointy #Truestruct Point{T} <: Pointy{T}x::Ty::T
end

元组类

元组和数组不同,元组不可更改,只可以插入和删除。
元组用( )开辟。

 typeof(("1",2,3)) #Tuple{String,Int64,Int64}

可变参数元组类

通过Vararg{T} 可以开辟0-任意个T类型元组。

mytype = Tuple{AbstractString,Vararg{Int}} #Tuple{AbstractString,Vararg{Int64,N} where N}
isa(("1",), mytupletype) #True

也可以用Vararg{Int,N} 开辟特定N个。例如

mytype = Tuple{AbstractString,Vararg{Int,3}} #Tuple{AbstractString,Int64,Int64,Int64}

UnionAll类

前面讲类的参数化时,有说到当对于有参数化的类,所有参数类都属于该类的子类,例如

 Type{Float64} <: Type #True

这里Type就相当于Union{ Type{T} where T} 也就是UnionAll类。例如Ptr可以更精确的写为Ptr{T} where T 表示所有可能的T组成的UnionAll类。

对于多参数情况,如Array{T,N} 可以固定一个,如Array{T, 1} where T代表所有的一维数组构成的类。

还可以固定范围,如

 Array{T} where T <: Real

对类的操作

由于类本身也是对象,此常规函数也可以做用在类上,比如<:函数就代表了类的包含关系。

 isa(1, Int) #Truetypeof(Int) #Datatype  type由于是object 仍然拥有type typeof(Union{Real,String}) # Uniontypeof(Union) #dataTypesupertype(Float64) #AbstractFloatsupertype(AbstractFloat) #Realsupertype(Real) #Numbersupertype(Number) #Any

优化默认输出

我们经常有改变类输出风格的需求,这个可以通过重载show函数来完成,比如我给出一个表达复数的组合类,想以极坐标的形式输出

struct Polar{T <: Real} <:Numberr::Tθ::T
end

可以通过

Base.show(io::IO, z::Polar) = print(io, z.r, " * exp(", z.θ, "im)")
Polar(3.0,4.0) #3.0 * exp(4.0im)

得到。还可以增加文字说明

Base.show(io::IO, ::MIME"test/plain", z::Polar{T}) where{T} = print(io, "Polar {$T} complex number:\n   ", z)
Polar(3.0,4.0) # Polar{Float64} complex number:3.0 * exp(4.0im)
module gsonstruct Goonameage::Int8endfunction tojson()println("I'am toJson method")return "I'am toJson"endfunction tojsonWith(obj::gson.Goo)println("I'am toJson method with ",obj.name)end
end

以上代码可以直接保存为gson.jl,里面关键字,module struct end function。

module:表示一个模块

struct:表示一个结构体,跟C类似

end:一个代码体的结束 类似 }

function:定义函数的关键字

x::V变量定义和类型约束

Julia不允许在结构体里定义函数,至少目前我没成功过,想要实现Java那种 obj.method()形式的调用,不太可能。(Java Bean 里是可以定义字段和方法的)

但是,julia有module这个神奇的东西。代码里是一个module包含了一个结构体和两个方法。那么如何使用呢?

建立一个Boot.jl,代码如下

include("gson.jl")
gson.tojson()
goo=gson.Goo("google",32)
gson.tojsonWith(goo)

到此为止,看代码就应该能懂Julia是如何面向对象了。而且还出现了include这个神奇的关键函数。一股子C语言的浓烈味道会不会把你吸引呢?

另外的说明:

多态

Julia没有class,但是在Julia里你也可以认为一切都是object,而这些object都是某个类型的实例。一个Julia的复合类型(Composite Type)可以这样声明,几乎和C是一样的。而实际上在Julia里类型分为复合类型(Composite Type),基础类型(Primitive Type)等等,本文不会介绍基础语法,还请参考官方文档(英文)。

struct Kittyname::String
end

那么这样Julia就不能像Python/C++等语言一样去通过让某个method属于一个class来实现多态。因为类型除了自己的constructor以外是不允许含有其它方法的。Julia使用了多重派发来解决这个问题。什么是多重派发?可以参见我另外一篇文章:PyTorch源码浅析(五)

于是在Julia里,method便是类型(type)和类型之间的相互作用(interaction),而非类(class)对其它类之间的作用。对于传统的OOP的优缺点在知乎上已经有过很多讨论了,在数学,物理等科学计算领域,我们往往需要定义很多复杂的关系,在概念上这样的方式更加直接,OOP在很多科学计算的场景下并不是很合适。Julia这种基于多重派发和类型的OO不妨是一种更加合适的尝试。例如,一个浮点数和整数的加法

+(lhs::Int32, res::Float64) = # ...

这个加法并不属于Int类型也不属于Float,这在数学上很讲得通。总体来讲,用Julia为理论对象进行抽象会非常自然。

然后如果你使用jupyter notebook就还会发现,由于method不再属于一个class,方法和类型之间的耦合更小。你可以先定义一些方法,然后在cell运行一下,然后再定义一些方法,而不需要再class中将所有的方法都声明完。

类型系统

仅仅有多重派发只能提供一些多态,但是无法实现类似继承的功能。这一点由类型系统来完成,但是请切记,不要将传统OOP里继承的思想搬过来,这是我接触地很多Julia的初学者,尤其是从Python/C++转来的初学者会发生的错误。这样的代码很不Julian,因为语言本身并没有继承的概念而且你会发现最后会导致自己手动复制代码从而造成大量的重复代码。当然如果你想去写类似OOP的代码风格的Julia,当然是可以做到的,但我不建议这么做。

首先简要回顾一下类型系统。Julia的类型系统是由抽象类型和实际类型(Concrete Type)构成的类型树。子类型会获得父类型行为,而不会获得父类型的成员。所以Julia是鸭子类型(Duck Type)的。在文档中,Julia Team强调说:我们更加在意类型的行为,而不是它的成员,所以无法继承成员是设计成这样的。

很多人都问过我,那么如果我有一些公共的成员需要各个子类型都有怎么办?如何少些重复代码?下面我来讲几种方案,请针对自己的情况去选择

  1. 公共的成员是一些静态成员(不是type trait)

定义共享的行为,而不是共享的成员

abstract type A end
struct B <: A end
struct C <: A endname(::A) = "no name" # 默认没有名字
name(::B) = "name B" # B 是另外的名字,而C就继承了A的行为

2. 成员是完全一样的,但是行为有所不同

使用Symbol作为标签来分发不同的行为,但是它们共享一个参数类型。

struct A{Tag}name::String
endname(x::A{:boy}) = "the boy's name is $(x.name)"
name(x::A{:girl}) = "the girl's name is $(x.name)"

3. 成员不同,部分是公共的,并且不是静态的

这种情况下,我们依然是通过行为去定义类的结构。我们需要有一个公共的method作为interface,这样我们就不必去管类里具体的结构。虽然不可避免地你需要用一个新的类型封装一下公共的成员,或者你需要手写一遍。

struct Am1m2name
endname(x::A) = x.namestruct Bm1name
endname(x::B) = x.name

所以使用类型的时候,我们不鼓励通过 . 来调用类型成员,我们鼓励去调用某个method,也就是使用类型的行为。不过实际上在具体实现的时候,通过合理地解耦,你会发现第三种情况实际上出现地相对较少,更多出现的是一二两种情况。如果你遇到了第三种情况不妨再思考思考。

以上经验,总结一下就是:在Julia里行为(behaviour)比其它的事情更加重要。而类型仅仅是用来派发行为的一种标签。

Julia面向对象(多重派发)相关推荐

  1. 有人说Julia比Python好,还给出了5个理由

    Julia 是一种多范式的函数式编程语言,用于机器学习和统计编程.尽管 Python 通常被认为是一种面向对象的编程语言,其实它也是用于机器学习的多范式编程语言.需要注意的是,Julia 语言更多地基 ...

  2. julia与python对比_有人说Julia比Python好,还给出了5个理由

    选自medium 作者:Emmett Boudreau 机器之心编译 参与:杜伟.张倩.肖清 本文作者从速度.通用性.多重派发.适用于 ML 的程度和包管理器 5 个方面阐述了 Julia 语言相较于 ...

  3. 技术▍Julia 解决了 C++/Python/Matlab 的哪些痛点?

    图为使用Julia画出来的图 本文作者|罗秀哲  编辑|布袋熊  36大数据获授权发布 机器学习 高性能 GPU 计算的编程语言 昨天我们推送一篇关于"Julia"的文章:< ...

  4. 机器学习LDA-基础Julia与Python的实现

    最近在看一本名为机器学习公式推导与代码实现的书,书中有用Numpy实现的LDA算法,具体Python代码如下 import numpy as np from sklearn import datase ...

  5. 全面对比 MATLAB、Julia、Python,谁在科学计算中更胜一筹?

    数百种编程语言,各有优劣,各自也都有自己最为适用的场景.那么就科学计算领域而言,主流的 MATLAB.Julia.Python 会有哪些最为独特的优势呢?又存在哪些让开发者无力的缺陷?在本文中,我们将 ...

  6. python 对比matlab_全面对比 MATLAB、Julia、Python,谁在科学计算中更胜一筹?

    原标题:全面对比 MATLAB.Julia.Python,谁在科学计算中更胜一筹? 数百种编程语言,各有优劣,各自也都有自己最为适用的场景.那么就科学计算领域而言,主流的 MATLAB.Julia.P ...

  7. Julia ---- 为Julia做一下辩解

    我写这篇文章的主要目的就是为了给我喜欢的Julia语言一辩,并且指出人们对Julia语言的几个常见的误区. 预警:文章非常长,所以需要希望入坑的人有耐心阅读 文章内容 1.常见误区 2.重新认识Jul ...

  8. 一个简单的Julia教程

    原文链接:点击打开链接 摘要: 当前版本 v0.5 因为在知乎上写的量子计算札记会涉及到使用Julia语言的数值模拟,同时随着中国的Julian越来越多,而之前几个在JuliaCN活跃的老司机最近一直 ...

  9. xx是一个类型 这在给定的上下文_基于上下文的派发:挂起临时变量内存

    最近做一些蒙卡相关的东西,然后遇到有一个可能很多人都会遇到的问题: 把所有的步骤都手写成原地(in-place)操作由于需要自己来保管各种中间变量会很麻烦(增加心智负担),但是用比较正常的方式去写又由 ...

最新文章

  1. html中加载shp文件,运用shapefile.js解析Shp文件
  2. 小朋友的视频直播之 nginx-rtmp
  3. 【量化交易】组合优化三部曲:换手率和alpha模型换手约束下的最优模型时变IC下的多空/多头最优组合换手率
  4. 网校mysql设计规范_网校数据库设计
  5. 消息队列入门案例-环境搭建
  6. php mariadb 查询语法,从PHP调用的查询中的MySQL(MariaDB)执行超时
  7. windows XP系统下oracle完整卸载过程
  8. android技术积累,Android开发中积累的一些报错的解决方法
  9. Canal 1.1.5 启动报错:caching_sha2_password Auth failed
  10. 5.10地址信息函数
  11. java post加密_使用Java和PHP的BCrypt,发送加密的密码并对其进行解码 - java
  12. 教你查看传说中的WPS2005彩蛋
  13. 系统越用越臃肿,你需要的系统瘦身技巧.
  14. @media 如何使用?
  15. 苹果Mac电脑文件夹路径怎么看?“访达”也能显示文件路径
  16. wordpress博客 qq客服插件
  17. 从0到1的电商架构应该怎么做?
  18. 很短,很文艺,很唯美的英语美句
  19. mr.baidu.com百度官方缩短网址接口网站调用生成制作方法解析
  20. app模式会被第三方平台模式取代吗_手机 App 不能取代第三方浏览器的原因是什么?...

热门文章

  1. Linux系统编程7:入门篇之Linux项目自动化构建工具-Make/Makefile的超强使用指南
  2. LeetCode 215 数组中的第K个最大元素
  3. 面试题 03.02. 栈的最小值/面试题30. 包含min函数的栈/155. 最小栈
  4. cuckoo沙箱常见报错总结
  5. Python url中提取域名(获取域名、获取顶级域名、tldextract)
  6. Linux查看二进制文件hexeditor
  7. python 标准库之 glob 介绍(获取文件夹下所有同类文件)
  8. ssm整合之web.xml配置
  9. Python实现抓取CSDN博客首页文章列表
  10. Ubuntu 12.04安装Microsoft lifecam studio摄像头