ENVI IDL:如何基于面向对象思想进行编程?

2023-10-22 22:01

本文主要是介绍ENVI IDL:如何基于面向对象思想进行编程?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

最近打算使用markdown语法进行博客的编写,所以风格和格式方面会有区别,见谅。

01 为什么会有这方面的想法?

我惯用python,因此对于IDL进行编程也会有先入为主的想法,它也体现在我的IDL编程中。

02 如何正常编写函数?

举例如下:

当我传入年月日(X年Y月Z日),返回年积日(第X年第Y天);当我传入年积日,返回年月日;

如果使用函数编写,一般需要两个函数完成上述功能。分别如下:

function ymd2doy, year, month, daynow_date = imsl_datetodays(day, month, year)old_date = imsl_datetodays(31, 12, year - 1)doy = now_date - old_datereturn, [year, doy]
endfunction doy2ymd, year, doyold_date = imsl_datetodays(31, 12, year - 1)imsl_daystodate, old_date + doy, day, month, yearreturn, [year, month, day]
end

至于其中两个函数imsl_datetodays, imsl_daystodate,前者是将年月日数据转化为儒略日(距离1900年1月1日的总天数),后者将儒略日转化为年月日。但如何将年月日和年积日进行互相转化这里稍微取巧,详见程序。

03 如何使用对象思想编写时间类?

3.1 编写具备简单方法的时间类

首先可以发现,这两个函数都是和时间相关的,所以自然就会萌生这样的想法?以后关于时间处理的函数我都放到一个类中,需要就去调用这个类就好了。

那么使用面向对象程序应该如何进行类的创建以及类的实例化呢?

首先,理解如何定义一个空类?(没有任何方法和属性)

pro time__definestruct = {time, inherits idl_object}
end

(文件名称一般命名为time_define.pro)

那么,首先定义一个pro过程:time__define,一般而言,__define是类定义的范式,注意有两个下划线__。前面的time为类的名称,与pro过程中的struct = {time, inherits idl_object}time对应。
inherits idl_object为继承基类(我个人认为idl_object为非常基础的类别),一般会继承该类别,该类别包含最基本的一些方法方便你去拓展类。

那么,将上述程序进行编译(compile),你可以使用该类了:
在这里插入图片描述
什么叫做实例化,类就是一类抽象的东西,他们具有相同或者类似的特征,但是在具体表达的时候又有一些差异。例如,我可以一个时间类,但是可能每一个时间对象都有不一样的年月日的属性;那么这个时候我们如果有很多个时间类的具体对象,那么不同对象就有不同的年月日属性,这个叫做实例化。

但是实例化出来这个类,我们没有为这个类写一些方法,因此这个时间对象a其实没有什么用。

现在需要为time类写一些方法(方法在普通的pro过程中就是函数,在类中称为方法)。

function time::doy2ymd, year, doyold_date = imsl_datetodays(31, 12, year - 1)imsl_daystodate, old_date + doy, day, month, yearreturn, [year, month, day]
endfunction time::ymd2doy, year, month, daynow_date = imsl_datetodays(day, month, year)old_date = imsl_datetodays(31, 12, year - 1)doy = now_date - old_datereturn, [year, doy]
endpro time__definestruct = {time, inherits idl_object}
end

如果是普通函数那么前面已经展示了,而对于time下的方法如何书写如上所示。
使用function class_name::function_name, arg1, arg2···样式进行书写。其中class_name表示类的名称或当前function归属于哪一个类;function_name为方法的名称;arg1,arg2为该方法的参数。::是将function_name作为class_name的方法的一种特殊表达字符,表示类拥有该方法。
那么接下来你可以使用该类。
在这里插入图片描述

3.2 编写具备方法和属性的时间类

前面已经说明了,如何进行具备方法的时间类编写,但是对象里面不仅可以有方法,还可以有关于对象的一些属性,例如时间类我们还可以编写具备年月日属性、具备年月日和年积日转化方法(两个)的时间类。

function time::doy2ymd, year, doyold_date = imsl_datetodays(31, 12, year - 1)imsl_daystodate, old_date + doy, day, month, yearreturn, [year, month, day]
endfunction time::ymd2doy, year, month, daynow_date = imsl_datetodays(day, month, year)old_date = imsl_datetodays(31, 12, year - 1)doy = now_date - old_datereturn, [year, doy]
end
pro time__definestruct = {time, inherits idl_object, year: 0l, month: 0l, day: 0l, doy: 0l}
end

这里我们在time_define中加上了三个属性: year: 0l, month: 0l, day: 0l, doy: 0l,均设置为默认值是长整型0。但是我们如何在类中去调用这些属性呢?
显然,在ymd2doy方法的内部中year、month、day的三个变量应该由用户传入,但是会不会存在歧义?这些变量似乎与类的
属性的名称完全一致,均为year, month, day。如何区分呢?

实际上,我们在类的内部使用类的属性时,通过self进行调用(对于类的方法也是如此)。例如我有一个方法打印时间对象当前的年月日,如何做到?

function time::print_timeprint, self.year, self.month, self.day, self.doy, format='年: %d,: %02d,: %02d, 年积日: %03d'
end

在这里插入图片描述

这样将引出一个问题?为什么我不可以直接使用a.year, a.month, a.day, a.doy进行这个时间对象属性的获取?

确实可以,但是如果基于现有代码那么将会发生报错:
在这里插入图片描述
这是因为,通过a.day进行属性的获取实际上是用到了基类idl_objectgetproperty方法,但是在idl_object基类中并没有关于新来的三个属性的相关代码,这里需要对该方法进行重写,如下:

(需要注意,getproperty方法在基类中是一个pro过程,而非function,所以进行重写时需要使用pro而非function

pro time::GetProperty, year=year, month=month, day=day, doy=doyyear = self.yearmonth = self.monthday = self.daydoy = self.doy
end

首先,需要明白get的含义:

当我们通过a.year获取a对象的year属性时,实际上是执行a.getproperty, year=year操作,在getproperty方法中,self.year将对象的属性返回给year,这样a.year就得到了这个对象的year属性。

读取对象属性的效果如下:
hi

但是这样写似乎会有一点小瑕疵,就是用户仅仅通过a.year获取对象的year属性,但是在getproperty方法中将每一个属性都返回了,虽然其他属性我们没有使用到。但是这会造成后续的一些麻烦,例如如果我后面get方法非常复杂的时候,那么所有的操作都执行一遍就会存在一些内存泄漏等问题或者说我在获取属性的时候又进行了一些比较复杂的计算那么就会浪费您昂贵的芯片。

所以我们需要有一个函数去监测当前参数是否被提供,或者说当前的year是否存在.注意或许会想到keyword_set函数,但是它用于检测参数是否被提供,也就是是否有东西传入进去?如果是下面这种情况:

function test, b=bprint, keyword_set(b), format='keyword_set: %d'b = 123
endpro temp_ = test(b=q)print, q
end

那么实际运行结果是:

在这里插入图片描述
所以应该使用arg_present函数,它用于检测是否有参数被传递进去(实际上并不准确,这里不详细说明)。如果无法理解就直接硬记住吧,虽然它实际上就是检测当前变量是否存在或者超出生存期(包括引用指针传入)。

修改后如下:

pro time::GetProperty, year=year, month=month, day=day, doy=doyif arg_present(year) then year = self.yearif arg_present(month) then month = self.monthif arg_present(day) then day = self.dayif arg_present(doy) then doy = self.doy
end

这里再说明一个初始化方法(init),这是在类被实例化时会触发的函数,用于对一些信息或者属性等进行初始化。
例如,前面的属性实际上是无法通过struct = {time, inherits idl_object, year: 0l, month: 0l, day: 0l, doy: 0l}进行最初的修改,
即便你已经填写了0,但是仅仅是声明了属性的数据类型。(即便将此处的数值进行修改,值不会发生变化,对于int就是默认的0,对于
string就是默认的空字符串等等)。

所以我们如果我们想要进行一些初始值的修改设定,需要通过inti方法。

function time::init, year, month, day, doy; 初始化self.year = yearself.month = monthself.day = dayself.doy = doyreturn, 1  ; 1表示成功实例化对象
end

上述代码中的yearmonthdaydoy参数在实例化时间类时需要传入。操作如下:

在这里插入图片描述

上述说明的完整代码如下:

function time::init, year, month, day, doy; 初始化self.year = yearself.month = monthself.day = dayself.doy = doyreturn, 1  ; 1表示成功实例化对象
endfunction time::doy2ymd, year, doyold_date = imsl_datetodays(31, 12, year - 1)imsl_daystodate, old_date + doy, day, month, yearreturn, [year, month, day]
endfunction time::ymd2doy, year, month, daynow_date = imsl_datetodays(day, month, year)old_date = imsl_datetodays(31, 12, year - 1)doy = now_date - old_datereturn, [year, doy]
endfunction time::print_timeprint, self.year, self.month, self.day, self.doy, format='年: %d, 月: %02d, 日: %02d, 年积日: %03d'
endpro time::GetProperty, year=year, month=month, day=day, doy=doyif arg_present(year) then year = self.yearif arg_present(month) then month = self.monthif arg_present(day) then day = self.dayif arg_present(doy) then doy = self.doy
endpro time__definestruct = {time, inherits idl_object, year: 0l, month: 0l, day: 0l, doy: 0l}
end

到这里基本上完成所有最基本类的创建和简单使用,以下是我对时间类的一些拓展:

function date::Init, _extra=ex; 初始化_ = self.idl_object::init()  ; 初始化父类对象if isa(ex) then self.setproperty, _extra=exreturn, 1  ; 1表示成功实例化对象
endfunction date::doy2ymd, year, doy, str=str, int=int; 更新if (keyword_set(year) and keyword_set(doy)) then beginself.year = yearself.doy = doyendif; 年积日转化为年月日old_date = imsl_datetodays(31, 12, self.year-1)imsl_daystodate, old_date + self.doy, self.day, self.month, self.yearreturn, self.to_ymd(str=str, int=int)
endfunction date::to_ymd, str=str, int=intif keyword_set(str) then return, string(self.year, self.month, self.day, format='%04d%02d%02d')if keyword_set(int) then return, long(string(self.year, self.month, self.day, format='%04d%02d%02d'))return, [self.year, self.month, self.day]
endfunction date::ymd2doy, year, month, day, str=str, int=int; 更新if (keyword_set(year) and keyword_set(month) and keyword_set(day)) then beginself.year = yearself.month = monthself.day = dayendif; 年月日转年积日now_days = imsl_datetodays(self.day, self.month, self.year)old_days = imsl_datetodays(31, 12, self.year - 1)self.doy = now_days - old_daysreturn, self.to_doy(str=str, int=int)
endfunction date::to_doy, str=str, int=intif keyword_set(str) then return, string(self.year, self.doy, format='%04d%03d')if keyword_set(int) then return, long(string(self.year, self.doy, format='%04d%03d'))return, [self.year, self.doy]
end; 更新年月日等属性
pro date::_update, ymd=ymd, doy=doyif keyword_set(ymd) then begin_ = self.ymd2doy(self.year, self.month, self.day)endifif keyword_set(doy) then begin_ = self.doy2ymd(self.year, self.doy)endif
end; 取值
pro date::GetProperty, year=year, month=month, day=day, doy=doy; 如果用户请求返回某一属性, 那么将其返回if arg_present(year) then year = self.yearIF arg_present(month) THEN month = self.monthIF arg_present(day) THEN day = self.dayif arg_present(doy) then doy = self.doy
end; 设置值
pro date::SetProperty, ymd=ymd, ydays=ydays; 如果用户传入属性, 那么就设置它if (isa(ymd, /integer) or isa(ymd, /string)) then beginif (isa(ymd, /integer)) then ymd = string(ymd, format='%08d')self.year = fix(strmid(ymd, 0, 4))self.month = fix(strmid(ymd, 4, 2))self.day = fix(strmid(ymd, 6, 2))self._update, /ymdendifif (isa(ydays, /integer) or isa(ydays, /string)) then beginif isa(ydays, /integer) then ydays = string(ydays, format='%07d')self.year = fix(strmid(ydays, 0, 4))self.doy = fix(strmid(ydays, 4, 3))self._update, /doyendif
end; 定义Date类
pro date__definestruct = {date, inherits idl_object, year: 0l, month: 0l, day: 0l, doy: 0l}
end

Bye~

这篇关于ENVI IDL:如何基于面向对象思想进行编程?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/263871

相关文章

Java并发编程之如何优雅关闭钩子Shutdown Hook

《Java并发编程之如何优雅关闭钩子ShutdownHook》这篇文章主要为大家详细介绍了Java如何实现优雅关闭钩子ShutdownHook,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起... 目录关闭钩子简介关闭钩子应用场景数据库连接实战演示使用关闭钩子的注意事项开源框架中的关闭钩子机制1.

利用python实现对excel文件进行加密

《利用python实现对excel文件进行加密》由于文件内容的私密性,需要对Excel文件进行加密,保护文件以免给第三方看到,本文将以Python语言为例,和大家讲讲如何对Excel文件进行加密,感兴... 目录前言方法一:使用pywin32库(仅限Windows)方法二:使用msoffcrypto-too

Pandas使用AdaBoost进行分类的实现

《Pandas使用AdaBoost进行分类的实现》Pandas和AdaBoost分类算法,可以高效地进行数据预处理和分类任务,本文主要介绍了Pandas使用AdaBoost进行分类的实现,具有一定的参... 目录什么是 AdaBoost?使用 AdaBoost 的步骤安装必要的库步骤一:数据准备步骤二:模型

使用Pandas进行均值填充的实现

《使用Pandas进行均值填充的实现》缺失数据(NaN值)是一个常见的问题,我们可以通过多种方法来处理缺失数据,其中一种常用的方法是均值填充,本文主要介绍了使用Pandas进行均值填充的实现,感兴趣的... 目录什么是均值填充?为什么选择均值填充?均值填充的步骤实际代码示例总结在数据分析和处理过程中,缺失数

QT进行CSV文件初始化与读写操作

《QT进行CSV文件初始化与读写操作》这篇文章主要为大家详细介绍了在QT环境中如何进行CSV文件的初始化、写入和读取操作,本文为大家整理了相关的操作的多种方法,希望对大家有所帮助... 目录前言一、CSV文件初始化二、CSV写入三、CSV读取四、QT 逐行读取csv文件五、Qt如何将数据保存成CSV文件前言

shell编程之函数与数组的使用详解

《shell编程之函数与数组的使用详解》:本文主要介绍shell编程之函数与数组的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录shell函数函数的用法俩个数求和系统资源监控并报警函数函数变量的作用范围函数的参数递归函数shell数组获取数组的长度读取某下的

通过Spring层面进行事务回滚的实现

《通过Spring层面进行事务回滚的实现》本文主要介绍了通过Spring层面进行事务回滚的实现,包括声明式事务和编程式事务,具有一定的参考价值,感兴趣的可以了解一下... 目录声明式事务回滚:1. 基础注解配置2. 指定回滚异常类型3. ​不回滚特殊场景编程式事务回滚:1. ​使用 TransactionT

Java中使用Hutool进行AES加密解密的方法举例

《Java中使用Hutool进行AES加密解密的方法举例》AES是一种对称加密,所谓对称加密就是加密与解密使用的秘钥是一个,下面:本文主要介绍Java中使用Hutool进行AES加密解密的相关资料... 目录前言一、Hutool简介与引入1.1 Hutool简介1.2 引入Hutool二、AES加密解密基础

SpringSecurity6.0 如何通过JWTtoken进行认证授权

《SpringSecurity6.0如何通过JWTtoken进行认证授权》:本文主要介绍SpringSecurity6.0通过JWTtoken进行认证授权的过程,本文给大家介绍的非常详细,感兴趣... 目录项目依赖认证UserDetailService生成JWT token权限控制小结之前写过一个文章,从S

使用Jackson进行JSON生成与解析的新手指南

《使用Jackson进行JSON生成与解析的新手指南》这篇文章主要为大家详细介绍了如何使用Jackson进行JSON生成与解析处理,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 核心依赖2. 基础用法2.1 对象转 jsON(序列化)2.2 JSON 转对象(反序列化)3.