代码复用技术—面向对象程序设计

类的定义与使用

  • Python使用class关键字来定义类,class关键字之后是一个空格,接下来时类的名字,如果派生自其他基类则需要把基类放到一对括号中并使用逗号分隔,然后是一个冒号,最后换行并定义类的内部实现
  • 类的首字母一般要大写
 class Car(object): #定义一个类,派生自object类
	def infor(self): #定义成员方法
		print("这是一个小汽车")
  • 定义类之后可以用来实例化对象,并可以通过“对象名.成员”的方式来访问其中的数据成员方法。
>>> car=Car() #实例化对象
>>> car.infor() #调用对象的成员方法
这是一个小汽车
  • 在Python中,可以使用内置函数isinstance()来测试一个对象是否为某个类的实例
  • 或者使用内置type()函数查看对象类型
>>> isinstance(car,Car)
True
>>> type(car)
<class '__main__.Car'>
  • Python提供了一个关键字pass,执行时什么也不会发生,表示空语句,可以使用pass来占位
>>> class Test:
	'''这是一个测试'''
	pass

>>> Test.__doc__ #查看类的帮助文档
'这是一个测试'

数据成员与成员方法

私有成员与公有成员

  • 私有成员在类的外部不能直接访问,一般是在类的内部进行访问和操作,或者在类的外部通过调用对象的公有成员方法来访问
  • 而公有成员可以是公开使用的,既可以在类的内部进行访问,也可以在外部程序中使用
  • 在定义类的成员时,如果成员名以两个(或更多)下划线开头但不是以两个(或更多)下划线结束则表示是私有成员,否则就不是私有成员
  • Python没有对私有成员提供严格的保护机制,通过一种特殊方式“对象名._类名__xxx”也可以在外部程序中访问私有成员,但会破坏类的封装性,不建议这样做
>>> class A:
	def __init__(self,value1=0,value2=0): #构造方法
		self._value1=value1
		self.__value2=value2 #私有成员
	def setValue(self,value1,value2): #成员方法,公有成员
		self._value1=value1
		sele.__value2=value2 #在类内部可以直接访问私有成员
	def show(self):
		print(self._value1)
		print(self.__value2)

		
>>> a=A()
>>> a._value1 #在类外部可以直接访问非私有成员
0
>>> a._A__value2 #在类外部访问对象的私有数据成员
0
  • 圆点“.”是成员访问运算符,可以用来访问命名空间、模块或对象中的成员
  • 在对象或类名后面加上一个圆点“.”,都会自动列出其所有公开成员
  • 如果在圆点“.”后面再加一个下划线,则会列出该对象或类的所有成员,包括私有成员
  • 也可以使用内置函数dir()来查看指定对象、模块或命名空间的所有成员

成员名定义的特殊性

  • _xxx:以一个下划线开头,保护成员,只有类对象和子类对象可以访问这些成员,在类的外部一般不建议直接访问,在模块中使用一个或多个下划线开头的成员不能用“from module import*”导入,除非在模块中使用__all__变量明确指明这样的成员可以被导入
  • _xxx _ :前后两个下划线,系统定义的特殊成员
  • __xxx:以两个或更多下划线开头但不以两个或更多下划线结束,表示私有成员,一般只有类对象自己能访问,子类对象也不能访问该成员,但在对象外部可以通过“对象名._类名__xxx”这样的特殊方式来访问

数据成员

  • 数据成员可以分为两类:属于对象的数据成员和属于类的数据成员
  • 属于对象的数据成员一般在构造方法__init__()中定义,当然也可以在其他成员方法中定义,在定义和在实例方法中访问数据成员时以self作为前缀,同一个类的不同对象(实例)的数据成员之间互不影响
  • 属于类的数据成员是该类所有对象共享的,不属于任何一个对象
  • 在主程序中或类的外部,对象数据成员属于实例(对象),只能通过对象名访问
  • 而数据成员属于类,可以通过类名或对象名访问
>>> class Demo(object):
	total=0
	def __new__(cls,*args,**kwargs): #该方法在__init__()之前被调用
		if cls.total>=2: #最多允许创建2个对象
			raise Exception('最多只能创建2个对象')
		else:
			return object.__new__(cls)
	def __init__(self):
		Demo.total=Demo.total+1

		
>>> t1=Demo()
>>> t1
<__main__.Demo object at 0x0000000F646A4748>
>>> t2=Demo()
>>> t3=Demo()
Traceback (most recent call last):
  File "<pyshell#40>", line 1, in <module>
    t3=Demo()
  File "<pyshell#36>", line 5, in __new__
    raise Exception('最多只能创建2个对象')
Exception: 最多只能创建2个对象
  • _new _():类的静态方法,用于确定是否要创建对象
  • _init _():构造方法,创建对象时自动调用
  • _del _():析构方法,释放对象时自动调用

成员方法、类方法、静态方法、抽象方法

  • 在面向对象程序设计中,函数和方法这两个概念是有本质区别的
  • 方法一般指与特定实例绑定的函数,通过对象调用方法是,对象本身将被作为第一个参数自动传递过去
  • 普通函数不具备这个特点
  • Python类的成员方法大致可以分为公有方法、私有方法、静态方法、类方法和抽象方法
  • 公有方法、私有方法和抽象方法一般是指属于对象的实例方法
  • 私有方法的名字以两个或更多的下划线开始
  • 抽象方法一般定义在抽象类中并且要求派生类必须重新实现
  • 每个对象都有自己的公有方法和私有方法,在这两类方法中都可以访问属于类和对象的成员
  • 公有方法通过对象名直接调用
  • 私有方法不能通过对象名直接调用,只能在其他实例方法中通过前缀self进行调用或在外部通过特殊的形式来调用

实例方法

  • 所有实例方法(包括公有方法、私有方法、抽象方法和某些特殊方法)都必须至少有一个名为self的参数,并且必须是方法的第一个形参(如果有多个形参),self参数代表当前对象
  • 实例方法中访问实例成员时需要以self为前缀
  • 在外部通过对象名调用对象方法时并不需要传递这个参数
  • 如果在外部通过类名调用属于对象的公有方法,需要显示为该方法的self参数传递一个对象名,用来明确指定访问哪个对象的成员

静态方法与类方法

  • 静态方法与类方法都可以通过类名和对象名调用,但不能直接访问属于对象的成员,只能访问属于类的成员
  • 类方法一般以cls作为第一个参数表示该类自身,在调用类方法时不需要为该参数传递值
  • 静态方法可以不接受任何参数
>>> class Root:
	__total=0
	def __init__(self,v): #构造方法
		self.__value=v
		Root.__total +=1
	def show(self): #普通实例方法,一般以self参数作为第一个参数名字
		print('self.__value:',self.__value)
		print('Root.__total:',Root.__total)
	@classmethod #修饰器 声明类方法
	def classShowTotal(cls): #类方法,一般以cls作为第一个参数名字
		print(cls.__total)
	@staticmethod # 修饰器 声明静态方法
	def staticShowTotal(): #静态方法 可以没有参数
		print(Root.__total)

	
>>> r=Root(3)
>>> r.classShowTotal() #通过对象来调用类方法
1
>>> r.staticShowTotal() #通过对象来调用静态方法
1
>>> rr=Root(5)
>>> Root.classShowTotal() #通过类名调用类方法
2
>>> Root.staticShowTotal() #通过类名调用静态方法
2
>>> Root.show(r) #可以通过这种方法来调用方法并访问实例对象
self.__value: 3
Root.__total: 2
>>> 

抽象方法

  • 抽象方法一般在抽象类中定义,并且要求在派生类中必须重新实现,否则不允许派生类创建实例
>>> import abc
>>> class Foo(metaclass=abc.ABCMeta): #抽象类
	def f1(self): #普通实例方法
		print(123)
	def f2(self): #普通实例方法
		print(456)
	@ abc.abstractmethod #抽象方法
	def f3(self): 
		raise Exception('你必须重写这个方法')

>>> class Bar(Foo):
	def f3(self): #必须重新实现基类中的抽象方法
		print(3333)

		
>>> b=Bar()
>>> b.f3()
3333

属性

  • 公开数据成员可以在外部随意访问和修改,数据很容易被破坏,而且不能保证合法性
  • 解决这类问题是定义私有成员,然后设计公开的成员方法来提供对私有数据成员的读取和修改操作
  • 属性是一种特殊形式的成员方法,结合了公开数据成员和成员方法的优点,既可以像成员方法那样对值进行必要的检查,又可以像数据成员一样灵活的访问
  • 可以将属性设置为可读、可修改、可删除
>>> class Text:
	def __init__(self,value):
		self.__value=value
	def __get(self):
		return self.__value
	def __set(self,v):
		self.__value=v
	def __del(self): #删除对象的私有数据成员
		del self.__value
	value=property(__get,__set,__del) #可读、可写、可删除的属性
	def show(self):
		print(self.__value)

		
>>> t=Text(3)
>>> t.show()
3
>>> t.value=5
>>> t.show()
5
>>> del t.value #删除相应的私有数据成员
>>> t.value #已删除,访问失败
Traceback (most recent call last):
  File "<pyshell#104>", line 1, in <module>
    t.value
  File "<pyshell#96>", line 5, in __get
    return self.__value
AttributeError: 'Text' object has no attribute '_Text__value'
>>> t.value=1 #动态增加属性和对应的私有数据成员
>>> t.show()
1
>>> t.value
1

类与对象的动态性、混入机制

  • 在Python中可以动态的为自定义类和对象增加数据成员和成员方法的行为成为混入机制
>>> import types
>>> class Car(object):
	price=1000 #属于类的数据成员
	def __init__(self,c):
		self.color=c #属于对象的数据成员

	
>>> car1=Car("Red") #实例化对象
>>> print(car1.color,Car.price) #访问对象和类的数据成员
Red 1000
>>> Car.price=100000 #修改类属性
>>> Car.name='QQ' #动态增加类属性
>>> car1.color='Yellow' #修改实例属性
>>> print(car1.color,Car.price,Car.name)
Yellow 100000 QQ
>>> def setSpeed(self,s):
	self.speed=s
>>> car1.setSpeed=types.MethodType(setSpeed,car1) #动态为对象增加成员方法
>>> car1.setSpeed(50) #调用对象的成员方法
>>> print(car1.speed)
50

继承和多态

继承

  • 设计一个新类是,如果可以继承一个已有的类进行二次开发,可以大大减少工作量
  • 在继承关系中,有已经设计好的类称为父类或基类
  • 新设计的类称为子类或派生类
  • 派生类可以继承父类的公有成员,但是不能继承其私有成员
  • 如果需要在派生类中调用基类的方法,可以使用内置函数super()或者通过“基类名.方法名()”的方法实现
>>> 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):
			raise Exception('名字必须是字符串')
		self.__name=name
	def setAge(self,age):
		if type(age) != int:
			raise Exception('年龄必须是数字')
		self.__age=age
	def setSex(self,sex):
		if sex not in ('man','woman') :
			raise Exception('性别必须是"man"或"woman"')
		self.__sex=sex
	def show(self):
		print(self.__name,self.__age,self.__sex,sep='\n')

#派生类		
>>> class Teacher(Person):
	def __init__(self,name=' ',age=30,sex='man',department='computer'): 
		super(Teacher,self).__init__(name,age,sex) #调用基类够着方法初始化基类的私有数据成员
		Person.__init__(self,name,age,sex) #也可以这样初始化基类的私有数据成员
		self.setDepartment(department) #初始化派生类的数据成员
	def setDepartment(self,department):
		if type(department)!=str:
			raise Exception('department必须是字符串')
		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('li si',32,'man','Math') #创建派生类对象
	lisi.show()
	lisi.setAge(40) #调用继承的方法修改年龄
	lisi.show()

	
zhang san
19
man
==============================
li si
32
man
Math
li si
40
man
Math
>>> 

多态

  • 多态指基类的同一个方法在不同派生类对象中具有不同的表现和行为
  • 派生类继承了基类的行为和属性之后,还会增加某些特定的行为和属性,同时还可能对继承来的某些行为进行一定的改变
>>> class Animal(object): #定义基类
	def show(self):
		print('我是一只动物')

		
>>> class Cat(Animal): #派生类,覆盖了基类的show()方法
	def show(self):
		print('我是一只猫')

		
>>> class Dog(Animal): #派生类
	def show(self):
		print('我是一只狗')

		
>>> class Test2(Animal): #派生类,没有覆盖基类的show()方法
	pass

>>> x=[item() for item in (Animal,Cat,Dog,Test2)] 
>>> for item in x: #遍历基类和派生类对象并调用show()方法
	item.show()

	
我是一只动物
我是一只猫
我是一只狗
我是一只动物
>>> 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值