Python从设计之初就已经是一门面向对象的语言,在python里所有东西皆是对象。
下面通过一个实例来说明什么是面向对象。
引子
你是一家公司的员工,公司现在要开发一款“人狗战争”的游戏,人狗战争肯定有人和狗两种角色。两种角色都有名字、性别,但是技能不同比如人可以用棍子打狗,狗可以咬人...
在不会面向对象之前倾尽所学知识写出下面代码。
def hit(name):
print('%s发动了打技能' % name)
def bite(name):
print('%s发动了咬技能' % name)
def Person(name,sex,hit):
data = {
'name': name,
'sex': sex,
'skill':hit
}
return data
def Dog(name,sex,dog_type,bite):
data = {
'name': name,
'sex': sex,
'dog_type':dog_type,
'skill':bite
}
return data
person = Person('林道明','男',hit)
person.get('skill')(person.get('name'))
dog.get('skill')(dog.get('name'))
"""
执行结果:
林道明发动了打技能
哈士奇小狗发动了咬技能
"""
这样写发现程序有很多相同的代码,而且拓展性不是很好(要是里面还出现其他n种动物类型,是不是要全部重写?),那么要这么写出拓展性好的代码呢?下面进入正题:
面向对象编程
Class 类
一个类即是对一类拥有相同属性的对象的抽象、蓝图、原型。在类中定义了这些对象的都具备的属性(variables(data))、共同的方法
Object 对象
一个对象即是一个类的实例化后实例,一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性,就像人类是指所有人,每个人是指具体的对象,人与人之前有共性,亦有不同
Encapsulation 封装
在类中对数据的赋值、内部调用对外部用户是透明的,这使类变成了一个胶囊或容器,里面包含着类的数据和方法
Inheritance 继承
一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承
Polymorphism 多态
多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现,这就是同一种事物表现出的多种形态。
编程其实就是一个将具体世界进行抽象化的过程,多态就是抽象化的一种体现,把一系列具体事物的共同点抽象出来, 再通过这个抽象的事物, 与不同的具体事物进行对话。
对不同类的对象发出相同的消息将会有不同的行为。比如,你的老板让所有员工在九点钟开始工作, 他只要在九点钟的时候说:“开始工作”即可,而不需要对销售人员说:“开始销售工作”,对技术人员说:“开始技术工作”, 因为“员工”是一个抽象的事物, 只要是员工就可以开始工作,他知道这一点就行了。至于每个员工,当然会各司其职,做各自的工作。
多态允许将子类的对象当作父类的对象使用,某父类型的引用指向其子类型的对象,调用的方法是该子类型的方法。这里引用和调用方法的代码编译前就已经决定了,而引用所指向的对象可以在运行期间动态绑定
OOP面向对象编程介绍
下面我们用面向对象重新实现一下“人狗战争”:
class Animal(object):
def __init__(self,name,sex):
self.name = name
self.sex = sex
class Person(Animal):
def __init__(self,name,sex):
super(Person,self).__init__(name,sex)
def hit(self):
print("%s发动了打技能" % self.name)
class Dog(Animal):
def __init__(self,name,sex,dog_type):
super(Dog,self).__init__(name,sex)
self.dog_type = dog_type
def bite(self):
print("%s发动了咬技能" % self.name)
person = Person('林道明','男')
dog = Dog('哈士奇小狗','母','哈士奇')
person.hit()
dog.bite()
"""
执行结果:
林道明发动了打技能
哈士奇小狗发动了咬技能
"""
通过面向对象的方法使程序更容易拓展,下面拓展一个“羊”类:
class Sheep(Animal):
def __init__(self,name,sex,hair_color):
super(Sheep,self).__init__(name,sex)
self.hair_color = hair_color
def bray(self):
print("%s发动了咬技能" % self.name)
封装:
封装最好理解了。封装是面向对象的特征之一,是对象和类概念的主要特性。封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
继承:
面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
通过继承创建的新类称为“子类”或“派生类”。
被继承的类称为“基类”、“父类”或“超类”。
继承的过程,就是从一般到特殊的过程。
要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。
在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。
继承概念的实现方式主要有2类:实现继承、接口继承。
OO开发范式大致为:划分对象→抽象类→将类组织成为层次化结构(继承和合成) →用类与实例进行设计和实现几个阶段。
多继承
需要注意圆括号中父类的顺序,若是父类中有相同的方法名,而在子类使用时未指定,python从左至右搜索 即方法在子类中未找到时,从左到右查找父类中是否包含方法。
方法重写:
class Animal(object):
def __init__(self,name,sex):
self.name = name
self.sex = sex
def talk(self):
pass
class Person(Animal):
def __init__(self,name,sex):
super(Person,self).__init__(name,sex)
def hit(self):
print("%s发动了打技能" % self.name)
def talk(self):
print('%s:oh fuck!' % self.name)
class Dog(Animal):
def __init__(self,name,sex,dog_type):
super(Dog,self).__init__(name,sex)
self.dog_type = dog_type
def bite(self):
print("%s发动了咬技能" % self.name)
def talk(self):
print('%s:汪汪汪!' % self.name)
class Sheep(Animal):
def __init__(self,name,sex,hair_color):
super(Sheep,self).__init__(name,sex)
self.hair_color = hair_color
def bray(self):
print("%s发动了咬技能" % self.name)
def talk(self):
print("%s:绵绵绵!" % self.name)
person = Person('林道明','男')
dog = Dog('哈士奇小狗','母','哈士奇')
person.talk()
dog.talk()
"""
执行结果:
林道明:oh fuck!
哈士奇小狗:汪汪汪!
"""
类私有&共开
两个下滑线(__)开头的变量和方法就是私有变量和私有方法 。公开和私有的区别就是类外可以访问公开变量和方法,而私有变量只能供类内部访问(子类外部都无法访问)。
class Dog2(object):
def __init__(self,name,type):
self.name = name #共开变量
self.__type = type #私有变量
#共开方法
def talk(self):
print('wang wang wang~~~')
#私有方法
def __kneel(self):
print('kneel down')
通过@staticmethod装饰器即可把其装饰的方法变为一个静态方法,什么是静态方法呢?其实不难理解,普通的方法,可以在实例化后直接调用,并且在方法里可以通过self.调用实例变量或类变量,但静态方法是不可以访问实例变量或类变量的,一个不能访问实例变量和类变量的方法,其实相当于跟类本身已经没什么关系了,它与类唯一的关联就是需要通过类名来调用这个方法
class mMath(object):
@staticmethod
def add(x,y,z):
print('%d+%d+%d=%d' % (x,y,z,x+y+z))
mMath.add(1,2,3)
属性方法
隐藏实现动作,所以你每次调用时,其实它都要经过一系列的动作才返回你结果,但这些动作过程不需要用户关心, 用户只需要调用这个属性就可以。
class School(object):
def __init__(self):
self.state = 0
@property
def aaa(self):
if self.state == 0:
print('上课')
elif self.state == 1:
print('下课')
elif self.state == 2:
print('返学')
elif self.state == 3:
print('放假')
@aaa.setter
def aaa(self,state):
print('修改状态为%s' % state)
self.state = state
@aaa.deleter
def schoolState(self):
print('清楚状态')
del self.state
s = School()
s.aaa
s.aaa = 2
s.aaa
反射
通过字符串映射或修改程序运行时的状态、属性、方法
class Dog(object):
def __init__(self,name):
self.name = name
def execute(self,str):
#判断是否存在字符和方法的映射
if hasattr(self,str):
# 得到字符映射的方法
func = getattr(self,str)
func()
def eat(self,food='checken'):
print('%s is eating %s' % (self.name,food))
def drink(self,drink='water'):
print('%s is drinking %s' % (self.name,drink))
dog = Dog('dog')
dog.execute('eat')
#设置成员
ret = setattr(dog,'name',19)
print(dog.name)
#删除成员
delattr(dog,'name')
#print(dog.name)#报错
类的特殊成员方法
1. __doc__ 表示类的描述信息
2. __module__ 和 __class__
__module__ 表示当前操作的对象在那个模块
__class__ 表示当前操作的对象的类是什么
3. __init__ 构造方法,通过类创建对象时,自动触发执行。
4.__del__析构方法,当对象在内存中被释放时,自动触发执行
5. __call__ 对象后面加括号,触发执行。
6. __dict__ 查看类或对象中的所有成员
8.__getitem__、__setitem__、__delitem__
类的专有方法
- __init__ : 构造函数,在生成对象时调用
- __del__ : 析构函数,释放对象时使用
- __repr__ : 打印,转换
- __setitem__ : 按照索引赋值
- __getitem__: 按照索引获取值
- __len__: 获得长度
- __cmp__: 比较运算
- __call__: 函数调用
- __add__: 加运算
- __sub__: 减运算
- __mul__: 乘运算
- __div__: 除运算
- __mod__: 求余运算
- __pow__: 乘方
运算符重载实例:
class mClock(object):
def __init__(self,mtime):
self.mtime = mtime
def __add__(self, other):
time1 = self.mtime.split(':')
time2 = other.mtime.split(':')
minute = (int(time1[1]) + int(time2[1]))%60
hour = (int(time1[0]) + int(time2[0]))%24 + int((int(time1[1]) + int(time2[1]))/60)
return '%s:%s' % (hour,minute)
mclock1 = mClock("12:45")
mclock2 = mClock("9:25")
print(mclock1 + mclock2)
"""
结果: 22:10
"""