面向对象程序设计
基础知识
如果某类派生自其他基类的话则需要把所有基类放到一对圆括号中并使用逗号分隔,然后是一个冒号,最后换行并定义类的内部实现。
eg:class Car():
def infor(self):
print(“This is a car”)
定义了类之后,就可以用来实例化对象,并通过“对象名.成员”的方法来访问其中的数据成员或成员方法。
eg:car=Car()
car.infor()
This is a car
Python提供了一个关键字pass,执行的时候什么也不会发生什么,可以用在类和函数的定义中或者选择结构中,表示空语句。
class A:
pass
可以用三引号为类进行必要的注释
calss Test:
‘’‘This is only a test’’’
私有成员与公有成员
在定义类的成员时,如果成员名以两个下划线(_ _)开头则表示是私有成员。
私有成员在类的外部不能直接访问,一般在类的内部进行访问和操作,或者在类外部通过调用对象的公有成员方法来访问。
Python提供了一种特殊方式"对象名._类名 _ _xxx"可以访问私有成员,但这会破坏类的封装性。
以下画线开头和结束的成员名有特殊的含义,类中使用下画线作为变量名和方法名前缀和后缀来表示类的特殊成员。
- _xxx:保护成员,不能用‘from module import *'导入,只有类对象和子类对象可以访问这些成员。
- _ _xxx _ _:系统定义的特殊成员。
- _ _xxx:类中的私有成员,一般只有类对象自己能访问,子类对象也不能访问到这个成员,但在对象外部可以通过“对象名. _ 类名 _ _xxx"这样的特殊方式来访问。
数据成员
数据成员可以分为两大类:属于对象的数据成员和属于类的数据成员。
- 属于对象的数据成员主要指在构造函数_ _init() _ _ 中定义的(当然也可以在其他成员方法中定义),定义和使用时必须以self作为前缀(这一点是必须的),同一个类的不同对象(实例)之间的数据成员之间互不影响。
- 属于类的数据成员是该类所有对象共享的,不属于任何一个对象,在定义类时这类数据成员不在任何一个成员方法的定义中。
- 在主程序中或类的外部,对象数据成员属于实例(对象),只能通过对象名访问。
- 类数据成员属于类,可以通过类名或对象名访问。
- 在Python中可以动态地为类和对象增加成员。
eg:class Car(object):
price=100000 #属于类的数据成员
def init(self,c):
self.color=c #属于对象的数据成员
car1=Car(“Red”)#实例化对象
print(car1.color,Car.price) #访问对象和类的数据成员
Car.price=110000 #修改类的属性
car1.color=“Yellow” #修改实例的属性
def setSpeed(self,s):
self.speed=s
import types
car1.setSpeed=types.MethodType(setSpeed,car1) #动态为对象增加成员方法
car1.setSpeed(50)
方法
在类中定义的方法可以粗略分为四大类:公有方法、私有方法、静态方法和类方法。
每个对象都有自己的公有方法和私有方法,在这两类方法中都可以访问属于类和对象的成员。
公有方法通过对象名直接调用,私有方法不能通过对象名直接调用,只能在实例方法中通过self调用或在外部通过Python支持的特殊方式调用。
类的所有实例方法都必须至少有一个名为self的参数,并且必须是方法的第一个形参(如果有多个形参的话),self参数代表对象自身。
在类的实例方法中访问实例属性时需要以self为前缀,但在外部通过对象名调用对象方法时并不需要传递这个参数,如果在外部通过类名调用属于对象的公有方法,需要显示为该方法的self参数传递一个对象名,用来明确指定访问哪个对象的数据成员。
静态方法和类方法都可以通过类名和对象名调用,但不能直接访问属于对象的成员,只能访问类的成员。一般将cls作为类方法的第一个参数,表示该类自身,在调用类方法时不需要为该参数传递值。
eg:
class Root:
__total=0
def init(self,v): #构造函数
self.__value=v
Root.__total+=1
def show(self):
print(‘self.__value:’,self.__value)
print(‘Root.__total’,Root.__total)
@classmethod #修饰器,声明静态方法
def classShowTotal(cls): #类方法
print(cls.__total)
@staticmethod #修饰器,声明静态方法
def staticShowTotal(): #静态方法
print(Root.__total)
r=Root(3)
r.classShowTotal()
r.staticShowTotal()
r.show()
rr=Root(5)
Root.classShowTotal()
Root.staticShowTotal()
Root.show ( r ) #通过类名调用属于对象的公有方法,需要显示为该方法的self参数传递
不同对象实例的数据成员之间互不影响,是不共享的。但在同一个类的所有实例方法
是在不同对象之间共享的,所有对象都执行相同的代码,通过self参数来判断要处理
哪个对象数据。
属性
公开的数据成员可以在外部随意访问和修改,很难控制用户修改时新数据的合法性。解决这一问题的常用方法是常用是定义私有数据成员,然后设计公开的成员方法来提供对私有数据成员的健壮性,保证了数据的完整性。
属性结合了公开数据成员和成员方法的优点,既可以像成员方法那样对值进行必要的检查,又可以像数据成员一样灵活地访问。
eg:
-
class Test:
def init(self,value):
self._ _value=value@property #修饰器,定义属性,提供对私有数据成员的访问
def value(self): #只读属性,无法修改和删除
return self._ _value
t=Test(3)
class Test:
def init(self,value):
self.__value=value
def __get(self): #读取私有数据成员的值
return self.__value
def __set(self,v): #修改私有数据成员的值
self.__value=v
value=property(__get,__set) #可读可写属性,指定相应的读写方法
def show(self):
print(self.__value)
t=Test(3)
print(t.value) #允许读取属性值
t.value=5 #允许修改属性值
print(t.value)
t.show() #属性对应的私有变量也得到了相应的修改
继承
派生类可以继承父类的公有成员,但是不能继承其私有成员。
如果需要在派生类中调用基类的方法,可以使用内置函数super()或者通过“基类名.方法名()"的方式来实现这一这一目的。
eg:
class Person(object):
#基类必须继承于object,否则在派生类中将无法使用super()函数
def init(self,name=’’,age=20,sex=‘man’):
self.setName(name) #通过调用方法进行初始化
self.setAge(age) #这样可以对参数进行更好地控制
self.setSex(sex)
def setName(self,name):
if not isinstance(name,str):
print(‘name must be string’)
self.__name=’’
return
self.__name=name
def setAge(self,age):
if type(age)!=int:
print(‘age must be interger.’)
self.__age=20
return
self.__age=age
def setSex(self,sex):
if sex not in(‘man’,‘woman’):
print(‘sex must be "man"or “woman”’)
self.__sex=‘man’
return
self.__sex=sex
def show(self):
print(self.__name,self.__age,self.__sex)
#派生类
class Teacher(Person):
def init(self,name=’’,age=30,sex=‘man’,deparment=‘Computer’):
#调用基类的构造方法初始化基类的私有数据成员
super(Teacher,self).init(name,age,sex)
#也可以这样初始化基类的私有数据成员
#Person.init(self,name,age,sex)
#初始化派生类的数据成员
self.setDepartment(deparment)
def setDepartment(self,department):
if type(department)!=str:
print(‘deparement must be a string.’)
self.__department=‘Computer’
return
self.__department=department
def show(self):
super(Teacher,self).show()
print(self.__department)
if name==‘main’:
#创建基类对象
zhangsan=Person(‘Zhang San’,19,‘man’)
zhangsan.show()
print(’=’*30)
#创建派生类对象
lisi=Teacher(‘LiSi’,32,‘man’,‘Math’)
lisi.show()
#调用继承的方法修改年龄
lisi.setAge(40)
lisi.show()
Python支持多继承,如果父类中有相同的方法名,而在子类中使用没有
指定父类名,则Python解释器将从左向右按顺序进行搜素。
下面代码完整地描述了类的继承机制,请认真体会构造函数、私有方法以及普通公开方法的继承原理。
eg:
class A():
def init(self):
self.__private()
self.public()
def __private(self):
print(’__private()method of A’)
def public(self):
print(‘public()method of A’)
def __test(self):
print(’__test()method of A’)
class B(A): #注意,B类没有构造函数
def __privatr(self):
print(’__private()method of B’)
def public(self):
print(‘public()method of B’)
b=B() #创建派生类对象
#b.test() #派生类没有继承基类的私有成员方法
class C(A):
def init(self):
self.__private()
self.public()
def __private(self):
print(’__private()method of C’)
def public(self):
print(‘public()method of C’)
c=C()
特殊方法与运算重载
构造函数是_ _init() _ _,一般用来为数据成员设置初始值或进行其他必要的初始化工作,在创建对象时被自动调用时和执行。如果用户没有设计构造函数,Python中的析构函数是 _ _del _ _(),一般用来释放对象占有的资源,在Python删除对象和回收对象空间时被自动调用和执行。
如果用户没有编写析构函数,Python将提供一个默认的析构函数进行必要的清理工作。
在Python中,除了构造函数和析构函数之外,还有大量的特殊方法支持更多的功能,运算符重载就是通过在类中重写特殊函数来实现的。
在自定义时如果重写了某个特殊方法即可支持对应的运算符,具体实现什么工作则完全可以根据需要来定义。
_ _ new_ _() | 类的静态方法,用于确定是否要创建对象 |
_ _init _ _() | 构造函数,生成对象时调用 |
_ _del _ _() | 析构函数,生成对象时调用 |
_ add_ _() | + |
_ _sub _ _() | - |
_ mul _ | * |
_ _truediv _ _() | / |
_ _floordiv _ _() | // |
_ _mod _ _() | % |
_ _pow _ _() | ** |
_ _repr _ _() | 打印、转换 |
_ _ setitem _ _() | 按照索引赋值 |
_ _getitem _ _() | 按照索引取值 |
_ _len() _ _ | 计算长度 |
_ _ call _ _() | 函数调用 |
_ _contains _ _ | in |
_ _eq _ _()、 _ _ ne _ _()、 _ _ lt _ _()、 _ _le _ _()、 _ _gt _ _()、 _ _ge _ _() | ==、!=、<、<=、>、>= |
_ _ str _ _() | 转换为 字符串 |
_ _ lshift _ _()、 _ _rshift _ _() | <<、>> |
_ _and _ _()、 _ _or _ _()、 _ _invert _ _()、 _ _xor _ _() | &、l、~、^ |
_ _ iadd _ _()、 _ _ isub _ _() | +=、-= |