R语言学习笔记——R语言面向对象编程系列2

最近在看任坤大神的新作——《R语言编程指南》,其中对于编程语言中非常流行的面向对象编程范式(OOP)在R语言中的实现进行了非常详尽的讲解,强烈推荐各位有志于进阶R语言编程的小伙伴儿进行阅读。

R语言内目前可以实现OOP范式的一共有四套标准:S3、S4、RC、R6,其中关于S3、S4两种范式在早期的各种扩展包中使用比较多,是基于泛型函数而实现的,之前在学习Python的面向对象编程系列时曾经做过粗浅的练习:

左手用R右手Python系列——面向对象编程基础

S3与S4之间的差异: 1.在定义S3类的时候,没有显式的定义过程,而定义S4类的时候需要调用函数setClass; 2.在初始化S3对象的时候,只是建立了一个list,然后设置其class属性,而初始化S4对象时需要使用函数new; 3.提取变量的符号不同,S3为$,而S4为@; 4.在应用泛型函数时,S3需要定义f.classname,而S4需要使用setMethod函数; 5.在声明泛型函数时,S3使用UseMethod(), 而S4使用setGeneric()。

S3的范式存在很大的隐患,对于类与对象的定义都不够严谨,S4范式在很大程度上弥补了S3的缺陷,但是在实现方式和方法分派上与主流的面向对象语言仍然存在很大的差距,方法分配、类与方法的定义都是割裂独立执行的,在封装上非常不方便,而RC以及在RC基础上进一步发展的R6标准已经逐步开始接近主流编程语言中面向对象的实现模式。

RC 是一种具有引用语义的类系统,它更像其他面向对象编程语言中的类系统。

它将所有的类属性及对应方法都封装在一个实例生成器中,通过生成器可以生成需要的实例,进而执行对应的类方法。在方法中修改字段的值,需要用<<-

以下是使用RC引用类实现的一个小爬虫:

#加载扩展包
library("RCurl")
library("XML")
library("magrittr")

首先定义类:

  • 类内包含必要的字段(其实就是数据抓取需要用到的参数
  • 定义要执行的方法(方法可以有多个)
hellobi <- setRefClass(
       "hellobi",
       fields = list(
                  i = "numeric", 
                  fullinfo = "data.frame",
                  headers  = "character"
                     ),
           methods = list(
                GetData = function() {
                     d      <- debugGatherer()
                     handle <- getCurlHandle(debugfunction=d$update,followlocation=TRUE,cookiefile="",verbose = TRUE)
                            while (i < 10){
                              i <<- i + 1
                              url <- sprintf("https://www.hellobi.com/jobs/search?page=%d",i)
                              tryCatch({
                                 content    <- getURL(url,.opts=list(httpheader=headers),.encoding="utf-8",curl=handle) %>% htmlParse() 
                                 job_item   <- content %>% xpathSApply(.,"//div[@class='job_item_middle pull-left']/h4/a",xmlValue)
                                 job_links  <- content %>% xpathSApply(.,"//div[@class='job_item_middle pull-left']/h4/a",xmlGetAttr,"href")
                                 job_info   <- content %>% xpathSApply(.,"//div[@class='job_item_middle pull-left']/h5",xmlValue,trim = TRUE) 
                                 job_salary <- content %>% xpathSApply(.,"//div[@class='job_item-right pull-right']/h4",xmlValue,trim = TRUE) 
                                 job_origin <- content %>% xpathSApply(.,"//div[@class='job_item-right pull-right']/h5",xmlValue,trim = TRUE)
                                 myreslut   <-  data.frame(job_item,job_links,job_info,job_salary,job_origin,stringsAsFactors = FALSE) 
                                  fullinfo <<- rbind(fullinfo,myreslut) 
                                 cat(sprintf("第【%d】页已抓取完毕!",i),sep = "\n")
                                   },error = function(e){
                                 cat(sprintf("第【%d】页抓取失败!",i),sep = "\n")
                               })
                              Sys.sleep(runif(1))
                           }
                        cat("all page is OK!!!")
                        return (fullinfo)
                       } 
                     )
          )

创建一个类实例:

mydata <- hellobi(
        i = 0, 
        fullinfo = data.frame(),
        headers = c(
                    Referer  = "https://www.hellobi.com/jobs/search",
                   `User-Agent` = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36"
                    )
         )

调用类中对应的方法执行爬虫程序:

mydatainfo <- mydata$GetData()

预览数据:

DT::datatable(mydatainfo)

R6是基于RC引用类系统的进一步升级版,它明确的的将类内所有的属性(字段)和方法进行了共有和私有的区分,这样可以控制那些对象对于用户是可见的,那些是不可见的,增加程序的安全性,并尽可能使得可见部分简洁明了,易于理解。

library("R6")
#R6不是内置包,是一个第三方扩展包,因此在使用R6系统前需要提前加载该包

创建R6对象:

  • 设置公有变量部分(内含可见的参数、初始化函数等)
  • 设置私有变量(内可以包含安全级别高的一些变量、参数、函数)
hellobi <- R6Class(
       "hellobi",
        public = list(
            i = NA,
            fullinfo = NA,
            headers  = NA,
            #初始化函数
            initialize = function(i,fullinfo,headers) {
                     #以下主要是进行参数检查并进行分配初始化参数
                     if (!missing(i))        self$i <- i
                     if (!missing(fullinfo)) self$fullinfo <- fullinfo
                     if (!missing(headers))  self$headers  <- headers
                     },
            #方法调用(这里我将爬虫程序定义在私有域内,然后在公有域内进行引用)
            GetData = function() {
                    private$Crawler()
                 }
            ),
          #定义私有域(这里私有域主要定义爬虫程序)
          private = list(
                 Crawler = function(){
                          d      <- debugGatherer()
                          handle <- getCurlHandle(debugfunction=d$update,followlocation=TRUE,cookiefile="",verbose = TRUE)
                          while (self$i < 10){
                             self$i <<- self$i + 1
                             url <- sprintf("https://www.hellobi.com/jobs/search?page=%d",self$i)
                              tryCatch({
                                 content    <- getURL(url,.opts=list(httpheader=self$headers),.encoding="utf-8",curl=handle) %>% htmlParse() 
                                 job_item   <- content %>% xpathSApply(.,"//div[@class='job_item_middle pull-left']/h4/a",xmlValue)
                                 job_links  <- content %>% xpathSApply(.,"//div[@class='job_item_middle pull-left']/h4/a",xmlGetAttr,"href")
                                 job_info   <- content %>% xpathSApply(.,"//div[@class='job_item_middle pull-left']/h5",xmlValue,trim = TRUE) 
                                 job_salary <- content %>% xpathSApply(.,"//div[@class='job_item-right pull-right']/h4",xmlValue,trim = TRUE) 
                                 job_origin <- content %>% xpathSApply(.,"//div[@class='job_item-right pull-right']/h5",xmlValue,trim = TRUE)
                                 myreslut   <-  data.frame(job_item,job_links,job_info,job_salary,job_origin,stringsAsFactors = FALSE) 
                                 self$fullinfo <<- rbind(self$fullinfo,myreslut) 
                                 cat(sprintf("第【%d】页已抓取完毕!",self$i),sep = "\n")
                                   },error = function(e){
                                 cat(sprintf("第【%d】页抓取失败!",self$i),sep = "\n")
                               })
                              Sys.sleep(runif(1))
                           }
                        cat("all page is OK!!!")
                        return (self$fullinfo)
                }
           )
       )

创建类实例:

mydata <- hellobi$new(
           i =0, 
           fullinfo = data.frame(),
           headers = c(
                Referer  = "https://www.hellobi.com/jobs/search",
               `User-Agent` = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36"
                  )
           )

调用类中的方法执行爬虫程序:

mydatainfo2 <- mydata$GetData()

关于面向对象的一些高级特性——继承、多态等属性,有待以后有更深理解之后再做分享,因为自己理解的不够深刻,今天也是抱着试一试的心态尝试着熟练二者的区别,如果各位对此有更加精辟的理解,欢迎交流分享。

原文发布于微信公众号 - 数据小魔方(datamofang)

原文发表时间:2018-02-18

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Python小屋

微课系列(四):Python中map对象的几种用法和注意事项

在Python中,map、filter、enumerate、zip、reversed等对象除了惰性求值之外,还有个共同的特点是“其中的元素只能使用一次”,这一点...

862
来自专栏工科狗和生物喵

【我的漫漫跨考路】数据结构·队列的链表实现

? 正文之前 今天看无穷级数这个数学内容实在看得头疼,索性看到八点多就不看了。愉快的写起了码,对我来说这个可有趣了!虽然有时候莫名其妙的就会Run succe...

2895
来自专栏韩伟的专栏

基于对象和面向对象

“基于对象”是“面向对象”一次动态化变迁,它依赖于现代语言的动态特性,让方法和属性统一起来;用组合取代继承;以函数对象查找取代多态的方法调用。这些变化让面向对象...

1.5K0
来自专栏玄魂工作室

如何学Python 第十八课 位运算符介绍

欢迎回来! 在我们上一次的培训课程中,我们介绍了类以及OOP如何使编程/脚本更容易。 今天我们将休息一下,并且介绍一些相当简单的按位运算符。 按位运算符相对简单...

3395
来自专栏王亚昌的专栏

C++多线程编程学习二 [类中封装互斥量的设计]

      之前我也提到过,如果一个类的数据成员中在多线程环境中可能会被竞争使用时,一定要在类中解决这个问题,而不是在代码编写过程中在每次使用时去申请或释放,这...

901
来自专栏互联网开发者交流社区

我个人对OOP的理解

1003
来自专栏带你撸出一手好代码

从PHP代码的细节说起

因为一个BUG, 我在一个摇摇欲坠,几乎碰一下就会散架的项目中某一个角落中发现下面这样一段代码 ? 这段程序与那个BUG有密切的关系。 我来回反复的捉摸这段代码...

4397
来自专栏编程

手把手教你半个小时用python语言编程出你的第一个程序

学习目标 知道有序的软件开发过程的步骤。 了解遵循输入、处理、输出(IPO)模式的程序,并能够以简单的方式修改它们。 了解构成有效Python标识符和表达式的规...

4055
来自专栏工科狗和生物喵

【计算机本科补全计划】C++牛客网试题习题解析

正文之前 一大早醒来,外面淅淅沥沥的雨绵绵的下着,床铺真的舒服,但是我也不能就在床上刷微博看小说吧,所以想起了昨晚下载的牛客网的APP,赶紧掏出我的大宝贝---...

3927
来自专栏C/C++基础

C++0x 通用属性

C++在不断的发展,但每一阶段的C++标准提供的功能都很难完全满足现实需求,于是为了弥补标准的不足或者扩增特性应用场景所需的特性,各大C++编译器厂商多多少少在...

1242

扫码关注云+社区

领取腾讯云代金券