第十二章 类-面向对象程序设计(类定义与初始化、类的私有属性和方法、类的继承)
在Python中其实所有的数据类型皆是对象,Python也允许程序设计师自创数据类型,这种自创的数据类型就是本章的主题类(class)。
一、类的语法定义如下:
class Classname(): #类名称第一个字母必须大写
statement1
...
statement2
在类中可以定义属性和方法。类内定义方法(method)的方式与定义函数的方式相同,但是不可以称之为函数(function),必须称之为方法(method),在程序设计时我们可以随时调用函数,但是只有属于该类的对象(object)才可调用相关的方法。若是想操作类的属性与方法首先需定义该类的对象(object)变量,可以简称对象,然后使用下列方式操作:object.类的属性、object.类的方法():
#类定义
class Banks():
#定义银行类别
title = "China Bank" #定义属性
def motto(self): #定义方法
return "财富人生"
userbank = Banks() #定义类对象
print(userbank.title)
print(userbank.motto())
建立类很重要的一个工作是初始化整个类,所谓的初始化类是在类内建立一个初始化方法(method),这是一个特殊方法,当在程序内定义这个类的对象时将自动执行这个方法。初始化方法有一个固定名称是“__init__( )”,写法是init左右皆是2个底线字符,init其实是initialization的缩写,通常又将这类初始化的方法称构造函数(constructor)。在这初始化的方法内可以执行一些属性变量设定。
class Banks():
#定义银行类
title = "China Bank" #定义属性
def __init__(self, uname, money): #初始化方法
self.name = uname #设定存款者名称
self.balance = money #设定所存金额
def save_money(self, money): #设计存款方法
self.balance += money
print("存款 ", money, " 完成")
def withdraw_money(self, money): #设计取款方法
self.balance -=money
print("取款 ", money, " 完成")
def get_balance(self): #获得当前存款余额方法
print(self.name, "目前余额是: ", self.balance)
userbank = Banks('huang', 100) #定义类对象
userbank.get_balance() #打印存款余额
userbank.save_money(300) #存款300
userbank.get_balance()
userbank.withdraw_money(200) #取款200
userbank.get_balance()
clientbank = Banks('lin', 200) #定义另一个类对象
clientbank.get_balance()
上述程序中定义Banks类的userbank对象和clientbank对象时,Banks类会自动启动__init__()初始化函数,在这个定义中self是必需的,同时需放在所有参数的最前面(相当于最左边),Python在初始化时会自动传入这个参数self,代表的是类本身的对象,未来在类内想要参照各属性与函数执行运算皆要使用self。在程序中还有一个get_balance(self)方法,在这个方法内只有一个参数self,所以调用时可以不用任何参数。
在先前程序的Banks类中title是设为“China Bank”,其实这是初始值的设定,通常Python在设初始值时是将初始值设在__init__( )方法内:
class Banks():
#定义银行类
title = "China Bank" #定义属性
def __init__(self, uname, money): #初始化方法
self.name = uname #设定存款者名称
self.balance = money #设定所存金额
self.title = "China Bank" #设定属性初始默认值
二、类的封装。类内的属性可以让外部引用的称公有(public)属性,而可以让外部引用的方法称公有方法。其实前面所使用的Banks类内的属性与方法皆是公有属性与方法。但是程序设计时可以发现,外部直接引用时也代表可以直接修改类内的属性值,这将造成类数据不安全。因此Python也提供一个私有属性与方法的观念,这个观念的主要精神是类外无法直接更改类内的私有属性,类外也无法直接调用私有方法,这个观念又称封装(encapsulation)。Python对于类内的属性增加了私有属性(private attribute)的概念,应用方式是定义时在属性名称前面增加__(2个底线),定义为私有属性后,类外的程序就无法引用了。类的私有方法(private method)与私有属性类似,只要在方法前面加上__(2个底线)符号即可,类外的程序就无法调用。
class Banks():
#定义银行类
def __init__(self, uname): #初始化方法
self.__name = uname #设定私有属性客户名称
self.__balance = 0 #设定私有属性存款余额
self.__title = "China Bank" #设定私有属性银行名称
self.__rate = 6.5 #设定私有属性汇率
self.__charge = 0.01 #设定私有属性换汇服务费
def save_money(self, money): #设计存款方法
self.__balance += money
print("存款 ", money, " 完成")
def withdraw_money(self, money): #设计取款方法
self.__balance -=money
print("取款 ", money, " 完成")
def get_balance(self): #获得当前存款余额方法
print(self.__name, "目前余额是: ", self.__balance)
def usa_to_rmb(self, usa_dollar): #美元兑人民币方法
self.result = self.__cal_rate(usa_dollar) #调用私有方法
return self.result
def __cal_rate(self, usa_dollar): #定义换汇私有方法
return int(usa_dollar * self.__rate * (1 - self.__charge))
userbank = Banks('huang') #定义类对象
userbank.get_balance() #打印存款余额
userbank.balance = 1000 #尝试在类外直接修改私有属性,实际无效
userbank.get_balance()
usa_dollar = 50
print(usa_dollar, " 美元可以兑换 ", userbank.usa_to_rmb(usa_dollar), " 人民币")
三、类的继承。在面向对象程序设计中类是可以继承的,其中被继承的类称父类(parent class)或基类(base class),继承的类称子类(childclass)或衍生类(derived class)。类继承的最大优点是许多父类的公有方法或属性,在子类中不用重新设计,可以直接引用。在程序设计时,基类(base class)必须在衍生类(derived class)前面,衍生类继承了基类的公有属性与方法,同时也可以有自己的属性与方法。整个程序代码结构如下:
class BaseClassName(): #先定义基类
baseclass statement
class DerivedClassName(BaseClassName): #再定义子类
derivedclass statement
基于保护原因,类外是无法直接取得类内的私有属性,即使是它的衍生类也无法直接读取,如果真要取得可以定义一个公有方法,使用return方式,传回私有属性内容。程序设计时,衍生类也可以有自己的初始化__init__( )方法,同时也有可能衍生类的属性与方法名称和基类重复,碰上这个状况Python会先找寻衍生类是否有这个名称,如果有则先使用,如果没有则使用基类的名称内容。
class Banks():
#定义银行类
def __init__(self, uname): #初始化方法
self.__name = uname #设定私有属性客户名称
self.__title = "China Bank" #设定私有属性银行名称
def bank_title(self): #获得私有属性的公有方法
return self.__title
class BeijingBanks(Banks):
#定义银行子类
def __init__(self, uname): #子类有自己初始化方法
self.__name = uname ##子类定义同名属性客户名称
self.__title = "China Bank -- Beijing" #子类定义同名属性银行名称
def bank_title(self): #子类定义同名公有方法
return self.__title
userbank = Banks('huang') #定义类对象
print("Banks: ", userbank.bank_title())
clientbank = BeijingBanks('lin')
print("BeijingBanks: ", clientbank.bank_title())
衍生类引用基类的方法时需使用super( ):
class Animals():
""" 动物类 """
def __init__(self, uname, age): #初始化方法
self.name = uname
self.age = age
def run(self):
print(self.name, " is running")
class Dogs(Animals): #继承动物类
""" 狗类 """
def __init__(self, dog_name, dog_age):
super().__init__("dog " + dog_name, dog_age) #调用父类的初始化方法
def dog_run(self):
super().run() #调用父类的方法
print("it is a dog")
mycat = Animals('lucy', 5)
print(mycat.name, " is ", mycat.age, " years old.")
mycat.run()
mydog = Dogs('lily', 8)
print(mydog.name, " is ", mydog.age, " years old.")
mydog.run()
mydog.dog_run()
衍生类如果想引用基类的属性,要不就是在初始化方法中显示设置super.__init__(),代表将基类的属性复制到衍生类中,要不就是完全不写衍生类的初始化方法(即使写空的初始化方法def __init__() : pass 也不行),否则就无法引用到父类的属性:
class Animals():
""" 动物类 """
def __init__(self, uname, age): #初始化方法
self.name = uname
self.age = age
def run(self):
print(self.name, " is running")
class Dogs(Animals): #继承动物类
""" 狗类 """
#完全不写子类的初始化方法
#def __init__(self, dog_name, dog_age):
# super().__init__("dog " + dog_name, dog_age) #调用父类的初始化方法
def dog_run(self):
print(self.name, " is running") #引用父类的属性
mycat = Animals('lucy', 5)
print(mycat.name, " is ", mycat.age, " years old.")
mycat.run()
mydog = Dogs('lily', 8)
print(mydog.name, " is ", mydog.age, " years old.")
mydog.run()
mydog.dog_run()
下列是一个三代同堂的程序,在这个程序中有祖父(Grandfather)类,它的子类是父亲(Father)类,父亲类的子类是Son类。其实Ivan要取得父亲类的属性很容易,可是要取得祖父类的属性时就会碰上困难,解决方式是在Father类与Ivan类的__init__( )方法中增加下列设定:super.__init__()
class Grandfather():
""" 祖父类 """
def __init__(self):
self.grandfathermoney = 10000 #祖父类资产
def get_info1(self):
print("grandfather's information")
class Father(Grandfather):
""" 父类 """
def __init__(self):
self.fathermoney = 8000 #父类资产
super().__init__() #复制基类属性
def get_info2(self):
print("father's information")
class Son(Father):
""" 子类 """
def __init__(self):
self.sonmoney = 500 #子类资产
super().__init__() #复制基类属性
def get_info3(self):
print("Son's information")
def get_money(self): #取得三代家庭资产明细
print("\n儿子资产:", self.sonmoney,
"\n父亲资产:", self.fathermoney,
"\n祖父资产:", self.grandfathermoney)
son = Son()
son.get_info3()
son.get_info2()
son.get_info1()
son.get_money()
假设有一个父亲(Father)类,这个父亲类有2个儿子,分别是SonA类和SonB类,如果SonB类想取得SonA类的属性attu,可以使用下列方式:SonA().attu
class Father():
""" 父类 """
def __init__(self):
self.fathermoney = 5000 #父类资产
class SonA(Father):
""" 子类 """
def __init__(self):
self.sonamoney = 500 #子类资产
super().__init__() #复制基类属性
class SonB(Father):
""" 子类 """
def __init__(self):
self.sonbmoney = 300 #子类资产
super().__init__() #复制基类属性
def get_money(self):
print("\n父亲资产:", self.fathermoney,
"\n大儿子资产:", SonA().sonamoney, #取得兄弟类属性
"\n小儿子资产:", self.sonbmoney)
son = SonB()
son.get_money()