面向对象编程——R用户Python指南8/10本文为R用户Python指南系列第8篇,Python代码主要取自《Python编程:从入门到实践》第9章,R代码根据Python代码复写。本文使用rmarkdown和prettydoc模板生成格式,代码块中#之后的内容为注释,##之后的内容为代码输出结果。本文使用的R包主要为stringr和R6,需预先加载。其中stringr主要用于处理字符串,R6主要用于创建R6类。
library(pacman)p_load(stringr,R6)学习内容:如何编写类;如何使用属性在类中存储信息,以及如何编写方法,以让类具备所需的行为;如何编写方法__init__(),以便根据类创建包含所需属性的实例;如何修改实例的属性,包括直接修改以及通过方法进行修改;使用继承可简化相关类的创建工作;将一个类的实例用作另一个类的属性可让类更简洁;通过将类存储在模块中,并在需要使用这些类的文件中导入它们,可让项目组织有序;学习python的标准库,并见识使用模块collections中的OrderedDict类的示例。1创建和使用类在面向对象编程中,你编写表示现实世界中的事务和情境的类,并基于这些类来创建对象。编写类时,你定义一大类对象都有的通用行为。基于类创建对象时,每个对象都自动具备这种通用行为,然后可根据需要赋予每个对象独特的个性。使用面向对象编程可模拟现实情境。根据类来创建对象被称为实例化。实例化:根据类来创建对象。属性:可通过实例访问的变量。方法:类中的函数。1.1创建Dog类R有S3类、S4类、RC类、R6类四种面向对象编程系统,其中R6类比较接近Python中的类。R6中的方法、属性不能使用-赋值,只能使用=。Python使用class关键字创建类,R6使用R6Class()函数创建类。定义一个Dog类,具有name和age属性,以及sit()和roll_over()方法。Python调用__init__()方法创建Dog实例时,每个与类相关联的方法调用都自动传递实参self,它是一个指向实例本身的引用,让实例能够访问类中的属性和方法,以self为前缀的变量都可供类中的所有方法使用。R在R6Class()函数的initialize参数中初始化属性。Python
classDog:"""一次模拟小狗的简单尝试"""def__init__(self,name,age):"""初始化属性name和age"""self.name=nameself.age=agedefsit(self):"""模拟小狗收到命令时蹲下"""print(f"{self.name}isnowsitting.")defroll_over(self):"""模拟小狗收到命令时打滚"""print(f"{self.name}rolledover!")R
Dog-R6Class("Dog",list(name=NULL,age=NA,#初始化属性name和ageinitialize=function(name,age=NA){self$name-nameself$age-age},#模拟小狗收到命令时蹲下sit=function(){cat(str_glue("{self$name}isnowsitting."),"\n")},#模拟小狗收到命令时打滚roll_over=function(){cat(str_glue("{self$name}rolledover!"),"\n")}))1.2根据类创建实例可将类视为有关如何创建实例的说明。Dog类是一系列说明,让Python知道如何创建表示特定小狗的实例。Python通过向类传递属性参数创建实例,R6通过$符号和new()方法向类传递属性参数创建实例。Python
my_dog=Dog(Willie,6)print(f"Mydogsnameis{my_dog.name}.")##MydogsnameisWillie.print(f"Mydogis{my_dog.age}yearsold.")##Mydogis6yearsold.R
my_dog-Dog$new(name="Willie",age=6)cat(str_glue("Mydogsnameis{my_dog$name}."),"\n")##MydogsnameisWillie.cat(str_glue("Mydogis{my_dog$age}yearsold."),"\n")##Mydogis6yearsold.1.2.1访问属性Python使用.访问实例的属性,R6使用$访问实例的属性。Python
my_dog.name##Williemy_dog.age##6R
my_dog$name##[1]"Willie"my_dog$age##[1]61.2.2调用方法Python使用.调用类中的方法,R6使用$调用类中的方法。Python
my_dog.sit()##Willieisnowsitting.my_dog.roll_over()##Willierolledover!R
my_dog$sit()##Willieisnowsitting.my_dog$roll_over()##Willierolledover!1.2.3创建多个实例可按需求根据类创建任意数量的实例。Python
my_dog=Dog(Willie,6)your_dog=Dog(Lucy,3)print(f"Mydogsnameis{my_dog.name}.")##MydogsnameisWillie.print(f"Mydogis{my_dog.age}yearsold.")##Mydogis6yearsold.my_dog.sit()##Willieisnowsitting.print(f"\nYourdogsnameis{your_dog.name}.")####YourdogsnameisLucy.print(f"Yourdogis{your_dog.age}yearsold.")##Yourdogis3yearsold.your_dog.sit()##Lucyisnowsitting.R
my_dog-Dog$new(Willie,6)your_dog-Dog$new(Lucy,3)cat(str_glue("Mydogsnameis{my_dog$name}."),"\n")##MydogsnameisWillie.cat(str_glue("Mydogis{my_dog$age}yearsold."),"\n")##Mydogis6yearsold.my_dog$sit()##Willieisnowsitting.cat(str_glue("Yourdogsnameis{your_dog$name}."),"\n")##YourdogsnameisLucy.cat(str_glue("Yourdogis{your_dog$age}yearsold."),"\n")##Yourdogis3yearsold.your_dog$sit()##Lucyisnowsitting.2使用类和实例你可以使用类来模拟现实世界中的很多情景。类编写好后,你的大部分时间都将花在使用根据类创建的实例上。你需要执行的一个重要任务是修改实例的属性。你可以直接修改实例的属性,也可以编写方法以特定的方式进行修改。2.1Car类创建Car类。Python
classCar:"""一次模拟汽车的简单尝试"""def__init__(self,make,model,year):"""初始化描述汽车属性"""self.make=makeself.model=modelself.year=yeardefget_descriptive_name(self):"""返回整洁的描述性信息"""long_name=f"{self.year}{self.make}{self.model}"returnlong_name.title()my_new_car=Car(audi,a4,)print(my_new_car.get_descriptive_name())##AudiA4R
Car-R6Class("Car",list(make=NULL,model=NULL,year=NA,initialize=function(make,model,year){self$make-makeself$model-modelself$year-year},get_descriptive_name=function(){long_name-str_glue("{self$year}{self$make}{self$model}")return(str_to_title(long_name))}))my_new_car-Car$new("audi","a4",)cat(my_new_car$get_descriptive_name())##AudiA42.2给属性指定默认值为Car类添加1个命名为odometer_reading的属性,其初始值总是为0。Python
classCar:def__init__(self,make,model,year):self.make=makeself.model=modelself.year=yearself.odometer_reading=0defget_descriptive_name(self):long_name=f"{self.year}{self.make}{self.model}"returnlong_name.title()defread_odometer(self):print(f"Thiscarhas{self.odometer_reading}milesonit.")my_new_car=Car(audi,a4,)print(my_new_car.get_descriptive_name())##AudiA4my_new_car.read_odometer()##Thiscarhas0milesonit.R
Car-R6Class("Car",list(make=NULL,model=NULL,year=NA,odometer_reading=0,initialize=function(make,model,year,odometer_reading){self$make-makeself$model-modelself$year-year},get_descriptive_name=function(){long_name-str_glue("{self$year}{self$make}{self$model}")return(str_to_title(long_name))},read_odometer=function(){cat(str_glue("Thiscarhas{self$odometer_reading}milesonit."),"\n")}))my_new_car-Car$new("audi","a4",)cat(my_new_car$get_descriptive_name(),"\n")##AudiA4my_new_car$read_odometer()##Thiscarhas0milesonit.2.3修改属性的值2.3.1直接修改属性的值将Car类的实例my_new_car的属性里程表读数设置为23。Python
classCar:def__init__(self,make,model,year):self.make=makeself.model=modelself.year=yearself.odometer_reading=0defget_descriptive_name(self):long_name=f"{self.year}{self.make}{self.model}"returnlong_name.title()defread_odometer(self):print(f"Thiscarhas{self.odometer_reading}milesonit.")my_new_car=Car(audi,a4,)print(my_new_car.get_descriptive_name())##AudiA4my_new_car.odometer_reading=23my_new_car.read_odometer()##Thiscarhas23milesonit.R
Car-R6Class("Car",list(make=NULL,model=NULL,year=NA,odometer_reading=0,initialize=function(make,model,year,odometer_reading){self$make-makeself$model-modelself$year-year},get_descriptive_name=function(){long_name-str_glue("{self$year}{self$make}{self$model}")return(str_to_title(long_name))},read_odometer=function(){cat(str_glue("Thiscarhas{self$odometer_reading}milesonit."),"\n")}))my_new_car-Car$new("audi","a4",)cat(my_new_car$get_descriptive_name(),"\n")##AudiA4my_new_car$odometer_reading-23my_new_car$read_odometer()##Thiscarhas23milesonit.2.3.2通过方法修改属性的值为Car类添加update_odometer方法。Python
classCar:def__init__(self,make,model,year):self.make=makeself.model=modelself.year=yearself.odometer_reading=0defget_descriptive_name(self):long_name=f"{self.year}{self.make}{self.model}"returnlong_name.title()defread_odometer(self):print(f"Thiscarhas{self.odometer_reading}milesonit.")defupdate_odometer(self,mileage):"""将里程表读数设置为指定值,禁止将里程表读数往回调"""ifmileage=self.odometer_reading:self.odometer_reading=mileageelse:print("Youcantroolbackanodometer!")my_new_car=Car(audi,a4,)print(my_new_car.get_descriptive_name())##AudiA4my_new_car.update_odometer(23)my_new_car.read_odometer()##Thiscarhas23milesonit.R
Car-R6Class("Car",list(make=NULL,model=NULL,year=NA,odometer_reading=0,initialize=function(make,model,year,odometer_reading){self$make-makeself$model-modelself$year-year},get_descriptive_name=function(){long_name-str_glue("{self$year}{self$make}{self$model}")return(str_to_title(long_name))},read_odometer=function(){print(str_glue("Thiscarhas{self$odometer_reading}milesonit."))},update_odometer=function(mileage){if(mileage=self$odometer_reading){self$odometer_reading-mileage}else{cat("Youcantroolbackanodometer!","\n")}}))my_new_car-Car$new("audi","a4",)cat(my_new_car$get_descriptive_name(),"\n")##AudiA4my_new_car$update_odometer(23)my_new_car$read_odometer()##Thiscarhas23milesonit.2.3.3通过方法对属性的值进行递增为Car类增加递增方法,更新里程表读数。Python
classCar:def__init__(self,make,model,year):self.make=makeself.model=modelself.year=yearself.odometer_reading=0defget_descriptive_name(self):long_name=f"{self.year}{self.make}{self.model}"returnlong_name.title()defread_odometer(self):print(f"Thiscarhas{self.odometer_reading}milesonit.")defupdate_odometer(self,mileage):ifmileage=self.odometer_reading:self.odometer_reading=mileageelse:print("Youcantroolbackanodometer!")defincrement_odometer(self,miles):"""将里程表读数增加制定的量"""self.odometer_reading+=milesmy_used_car=Car(subaru,outback,)print(my_used_car.get_descriptive_name())##SubaruOutbackmy_used_car.update_odometer(23_)my_used_car.read_odometer()##Thiscarhas23milesonit.my_used_car.increment_odometer()my_used_car.read_odometer()##Thiscarhasmilesonit.R
Car-R6Class("Car",list(make=NULL,model=NULL,year=NA,odometer_reading=0,initialize=function(make,model,year,odometer_reading){self$make-makeself$model-modelself$year-year},get_descriptive_name=function(){long_name-str_glue("{self$year}{self$make}{self$model}")return(str_to_title(long_name))},read_odometer=function(){cat(str_glue("Thiscarhas{self$odometer_reading}milesonit."),"\n")},update_odometer=function(mileage){if(mileage=self$odometer_reading){self$odometer_reading-mileage}else{cat("Youcantroolbackanodometer!","\n")}},increment_odometer=function(miles){self$odometer_reading=self$odometer_reading+miles}))my_used_car=Car$new(subaru,outback,)cat(my_used_car$get_descriptive_name(),"\n")##SubaruOutbackmy_used_car$update_odometer(23)my_used_car$read_odometer()##Thiscarhas23milesonit.my_used_car$increment_odometer()my_used_car$read_odometer()##Thiscarhasmilesonit.3继承编写类时,并非总是要从空白开始。如果你要编写的类是另一个现成类的特殊版本,可使用继承。一个类继承另一个类时,它将自动获得另一个类的所有属性和方法;原有的类称为父类,而新类称为子类。子类继承了其父类的所有属性和方法,同时还可以定义自己的属性和方法。3.1子类的方法__init__()Python通过super()方法继承父类的属性,R6通过R6Class()函数的inherit参数创建子类并继承父类的属性。Python
classElectricCar(Car):"""电动车的独特之处"""def__init__(self,make,model,year):"""初始化父类的属性"""super().__init__(make,model,year)my_tesla=ElectricCar(tesla,models,)print(my_tesla.get_descriptive_name())##TeslaModelSR
ElectricCar-R6Class("ElectricCar",inherit=Car)my_tesla-ElectricCar$new("tesla","models",)cat(my_tesla$get_descriptive_name())##TeslaModelS3.2给子类定义属性和方法R6通过set方法为类添加额外的属性和方法。Python
classElectricCar(Car):def__init__(self,make,model,year):"""初始化父类的属性,再初始化电动汽车特有的属性"""super().__init__(make,model,year)self.battery_size=75defdescribe_battery(self):"""打印一条描述电瓶容量的消息"""print(f"Thiscarhasa{self.battery_size}-kWhbattery.")my_tesla=ElectricCar(tesla,models,)print(my_tesla.get_descriptive_name())##TeslaModelSmy_tesla.describe_battery()##Thiscarhasa75-kWhbattery.R
ElectricCar-R6Class("ElectricCar",inherit=Car)#继承父类ElectricCar$set(public,battery_size,75)#添加属性ElectricCar$set(public,describe_battery,function(){cat(str_glue("Thiscarhasa{self$battery_size}-kWhbattery."),"\n")})#添加方法my_tesla=ElectricCar$new(tesla,models,)cat(my_tesla$get_descriptive_name(),"\n")##TeslaModelSmy_tesla$describe_battery()##Thiscarhasa75-kWhbattery.3.3重写父类的方法对于父类的方法,只要它不符合子类模拟的实物的行为,都可以进行重写。Python
classCar:def__init__(self,make,model,year):"""初始化描述汽车属性"""self.make=makeself.model=modelself.year=yearself.odometer_reading=0defget_descriptive_name(self):"""返回整洁的描述性信息"""long_name=f"{self.year}{self.make}{self.model}"returnlong_name.title()defread_odometer(self):"""打印汽车里程"""print(f"Thiscarhas{self.odometer_reading}milesonit.")defupdate_odometer(self,mileage):"""将里程表读数设置为指定值"""ifmileage=self.odometer_reading:self.odometer_reading=mileageelse:print("Youcantroolbackanodometer!")defincrement_odometer(self,miles):"""将里程表读数增加制定的量"""self.odometer_reading+=milesdeffill_gas_tank(self):print("Allcarsneedagastank!")#父类的方法classElectricCar(Car):def__init__(self,make,model,year):"""初始化父类的属性,再初始化电动汽车特有的属性"""super().__init__(make,model,year)self.battery_size=75defdescribe_battery(self):"""打印一条描述电瓶容量的消息"""print(f"Thiscarhasa{self.battery_size}-kWhbattery.")deffill_gas_tank(self):print("Thiscardoesntneedagastank!")#重写父类的方法R
Car-R6Class("Car",list(make=NULL,model=NULL,year=NA,odometer_reading=0,initialize=function(make,model,year,odometer_reading){self$make-makeself$model-modelself$year-year},get_descriptive_name=function(){long_name-str_glue("{self$year}{self$make}{self$model}")return(str_to_title(long_name))},read_odometer=function(){cat(str_glue("Thiscarhas{self$odometer_reading}milesonit."),"\n")},update_odometer=function(mileage){if(mileage=self$odometer_reading){self$odometer_reading-mileage}else{cat("Youcantroolbackanodometer!","\n")}},increment_odometer=function(miles){self$odometer_reading=self$odometer_reading+miles},fill_gas_tank=function(){cat("Allcarsneedagastank!","\n")}))ElectricCar-R6Class("ElectricCar",inherit=Car)ElectricCar$set(public,battery_size,75)ElectricCar$set(public,describe_battery,function(){cat(str_glue("Thiscarhasa{self$battery_size}-kWhbattery."),"\n")})ElectricCar$set(public,fill_gas_tank,function(){cat("Thiscardoesntneedagastank!","\n")})my_tesla=ElectricCar$new(tesla,models,)cat(my_tesla$get_descriptive_name(),"\n")##TeslaModelSmy_tesla$fill_gas_tank()##Thiscardoesntneedagastank!3.4将实例用作属性可以将类的一部分提取出来,作为一个独立的类。Python
classBattery:def__init__(self,battery_size=75):self.battery_size=battery_sizedefdescribe_battery(self):print(f"Thiscarhasa{self.battery_size}-kWhbattery.")defget_range(self):"""打印一条消息,指出电瓶的续航里程"""ifself.battery_size==75:rng=elifself.battery_size==:rng=print(f"Thiscarcangoabout{rng}milesonafullcharge.")classElectricCar(Car):def__init__(self,make,model,year):"""初始化父类的属性,再初始化电动汽车特有的属性"""super().__init__(make,model,year)self.battery=Battery()#将Battery()实例化并将该实例赋给属性self.batterymy_tesla=ElectricCar(tesla,models,)print(my_tesla.get_descriptive_name())##TeslaModelSmy_tesla.battery.describe_battery()##Thiscarhasa75-kWhbattery.my_tesla.battery.get_range()##Thiscarcangoaboutmilesonafullcharge.R
Battery-R6Class("Battery",lock_objects=F,list(battery_size=NA,initialize=function(battery_size=75){self$battery_size-battery_size},describe_battery=function(){cat(str_glue("Thiscarhasa{self$battery_size}-kWhbattery."),"\n")},get_rng=function(){if(self$battery_size==75){assign("rng",,envir=.GlobalEnv)}elseif(self$battery_size==){assign("rng",,envir=.GlobalEnv)}cat(str_glue("Thiscarcangoabout{rng}milesonafullcharge."),"\n")}))ElectricCar-R6Class("ElectricCar",inherit=Car)ElectricCar$set(public,battery,Battery$new())my_tesla=ElectricCar$new(tesla,models,)cat(my_tesla$get_descriptive_name(),"\n")##TeslaModelSmy_tesla$battery$describe_battery()##Thiscarhasa75-kWhbattery.my_tesla$battery$get_rng()##Thiscarcangoaboutmilesonafullcharge.4Python标准库Python通过import关键字导入库,通过from和import关键字导入库中的特定函数;R通过library()加载包,通过::在不加载包的情况下使用包中的函数,如purrr::is_empty()。Python
fromrandomimportrandintrandint(1,6)##3R
sample(1:6,size=1)##[1]1Python
fromrandomimportchoiceplayers=[charles,martina,michael,florence,eli]first_up=choice(players)first_up##eliR
players=c(charles,martina,michael,florence,eli)first_up=sample(players,size=1)first_up##[1]"charles"高恒恒