python面向对象
面向对象编程(Object-Oriented Programming )介绍
对于编程语言的初学者来讲,OOP不是一个很容易理解的编程方式,虽然大家都知道OOP的三大特性是继承、封装、多态,并且大家也 都知道了如何定义类、方法等面向对象的常用语法,但是一到真正写程序的时候,还是很多人喜欢用函数式编程来写代码,特别是初学者,很容易陷入一个窘境就是 “我知道面向对象,我也会写类,但我依然没发现在使用了面向对象后,对我们的程序开发效率或其它方面带来什么好处,因为我使用函数编程就可以减少重复代码 并做到程序可扩展了,为啥子还用面向对象?”。 对于此,我个人觉得原因应该还是因为你没有充分了解到面向对象能带来的好处。
无论用什么形式来编程,我们都要明确记住以下原则:
- 写重复代码是非常不好的低级行为
- 你写的代码需要经常变更
开发正规的程序跟那种写个运行一次就扔了的小脚本一个很大不同就是,你的代码总是需要不断的更改,不是修改bug就是添加新功能等,所以为了日后方便程序的修改及扩展,你写的代码一定要遵循易读、易改的原则(专业术语叫可读性好、易扩展)。
如果你把一段同样的代码复制、粘贴到了程序的多个地方以实现在程序的各个地方调用 这个功能,那日后你再对这个功能进行修改时,就需要把程序里多个地方都改一遍,这种写程序的方式是有问题的,因为如果你不小心漏掉了一个地方没改,那可能 会导致整个程序的运行都 出问题。 因此我们知道 在开发中一定要努力避免写重复的代码,否则就相当于给自己再挖坑。
还好,函数的出现就能帮我们轻松的解决重复代码的问题,对于需要重复调用的功能,只需要把它写成一个函数,然后在程序的各个地方直接调用这个函数名就好了,并且当需要修改这个功能时,只需改函数代码,然后整个程序就都更新了。
面向对象同面向过程相比可以使程序更加容易扩展和易更改。比如 古时候,人们打仗杀人都用刀,后来出来了枪,它的主要功能跟刀一样,也是杀人,然后有人就问,既然刀能杀人了,那还要枪干毛线,哈哈,显而易见,因为枪能更好更快更容易的杀人。
关于面向过程和面向对象区别参考:
http://www.cnblogs.com/luotianshuai/p/5007044.html
定义一个简单的CS游戏
#定义一个字典, 所有的角色的变量名都是一样的,但调用的时候又能区分开分别是谁。 roles = { 1:{'name':'Alex', 'role':'terrorist', 'weapon':'AK47', 'life_value': 100, 'money': 15000, }, 2:{'name':'Jack', 'role':'police', 'weapon':'B22', 'life_value': 100, 'money': 15000, }, 3:{'name':'Rain', 'role':'terrorist', 'weapon':'C33', 'life_value': 100, 'money': 15000, }, 4:{'name':'Eirc', 'role':'police', 'weapon':'B51', 'life_value': 100, 'money': 15000, }, } #print(roles[1])#Alex #print(roles[2]) #Jack def shot(by_who): #开了枪后要减子弹数 pass def got_shot(who): #中枪后要减血 who[‘life_value’] -= 10 pass def buy_gun(who,gun_name): #检查钱够不够,买了枪后要扣钱 pass
继续按照这个思路设计,再完善一下代码,游戏的简单版就出来了,但是在往下走之前,我们来看看上面的这种代码写法有没有问题,至少从上面的代码设计中,我看到以下几点缺陷:
1.无法确保新添加角色时,他们的属性定义是否正确。 比如 weapon 拼写错误程序无法检测; 2.角色间无法区分不同的功能 警察和匪徒有不同的功能用函数式编写大家都可以调用,无法区分; 3.添加新的属性都要添加一遍 假设要新添加防弹衣功能,需要在字典中每个角色都要添加; 4.每个功能使用相同属性都要重新传入一遍 buy_gun,got_shot每次调用都要传入name; 5.我们在上面定义了got_shot()后要减血,也就是说减血这个动作是应该通过被击中这个事件来引起的, 但其实我不通过got_shot(),直接调用角色roles[role_id][‘life_value’] 减血也可以.
使用面向对象实现:
class Role(object): def __init__(self,name,role,weapon,life_value=100,money=15000): self.name = name self.role = role self.weapon = weapon self.life_value = life_value self.money = money def shot(self): print("shooting...") def got_shot(self): print("ah...,I got shot...") def buy_gun(self,gun_name): print("just bought %s" %gun_name) r1 = Role('Alex','police','AK47’) #生成一个角色 r2 = Role('Jack','terrorist','B22’) #生成一个角色
先不考虑语法细节,相比函数式写法,上面用面向对象中的类来写最直接的改进有以下2点:
- 代码量少了近一半
- 角色和它所具有的功能可以一目了然看出来
面向对象三大特性
封装:
封装,顾名思义就是将内容封装到某个地方,以后再去调用被封装在某处的内容。
继承:
从一般到特殊的过程;它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
多态:
意味着可以对不同类的对象使用同样的操作。
封装
构造方法:
类在实例化 (把一个抽象的类变成一个具体的对象的过程叫做实例化 )对象的时候,会执行初始化方法。
class person(object): def __init__(self,name,age): self.name = name self.age = age obj1 = person('koka',24) <=> person.__init__(obj1,'koka',24) 解析: 你执行obj1 = person('koka',24)时,python的解释器其实干了两件事: 1.在内存中开辟一块空间指向obj1这个变量名 2.调用person这个类并执行其中的__init__(…)方法,相当于person.__init__(obj1,'koka',24),这么做是为什么呢? 是为了把'koka',24这2个值跟刚开辟的obj1关联起来,因为关联起来后,你就可以直接obj1.name, obj1.age 这样来调用啦。 所以,为实现这种关联,在调用__init__方法时,就必须把obj1这个变量也传进去,否则__init__不知道要把那2个参数跟谁关联呀。 所以这个__init__(…)方法里的,self.name = name , self.role = role 等等的意思就是要把这几个值存到obj1的内存空间里。
更多内容参考:http://www.cnblogs.com/wupeiqi/p/4493506.html
继承
继承,面向对象中的继承和现实生活中的继承相同,即:子可以继承父的内容。
例如:
猫可以:喵喵叫、吃、喝、拉、撒
狗可以:汪汪叫、吃、喝、拉、撒
如果我们要分别为猫和狗创建一个类,那么就需要为 猫 和 狗 实现他们所有的功能,如下所示:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class 猫: def 喵喵叫(self): print '喵喵叫' def 吃(self): # do something def 喝(self): # do something def 拉(self): # do something def 撒(self): # do something class 狗: def 汪汪叫(self): print '喵喵叫' def 吃(self): # do something def 喝(self): # do something def 拉(self): # do something def 撒(self): # do something 伪代码
上述代码不难看出,吃、喝、拉、撒是猫和狗都具有的功能,而我们却分别的猫和狗的类中编写了两次。如果使用 继承 的思想,如下实现:
动物:吃、喝、拉、撒
猫:喵喵叫(猫继承动物的功能)
狗:汪汪叫(狗继承动物的功能)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class 动物: def 吃(self): # do something def 喝(self): # do something def 拉(self): # do something def 撒(self): # do something # 在类后面括号中写入另外一个类名,表示当前类继承另外一个类 class 猫(动物): def 喵喵叫(self): print '喵喵叫' # 在类后面括号中写入另外一个类名,表示当前类继承另外一个类 class 狗(动物): def 汪汪叫(self): print '喵喵叫' 伪代码
class Animal: def eat(self): print "%s 吃 " %self.name def drink(self): print "%s 喝 " %self.name def shit(self): print "%s 拉 " %self.name def pee(self): print "%s 撒 " %self.name class Cat(Animal): def __init__(self, name): self.name = name self.breed = '猫' def cry(self): print '喵喵叫' class Dog(Animal): def __init__(self, name): self.name = name self.breed = '狗' def cry(self): print '汪汪叫' # ######### 执行 ######### c1 = Cat('小白家的小黑猫') c1.eat() c2 = Cat('小黑的小白猫') c2.drink() d1 = Dog('胖子家的小瘦狗') d1.eat()
所以,对于面向对象的继承来说,其实就是将多个类共有的方法提取到父类中,子类仅需继承父类而不必一一实现每个方法。
注:除了子类和父类的称谓,你可能看到过 派生类 和 基类 ,他们与子类和父类只是叫法不同而已。
学习了继承的写法之后,我们用代码来是上述阿猫阿狗的功能:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Animal: def eat(self): print "%s 吃 " %self.name def drink(self): print "%s 喝 " %self.name def shit(self): print "%s 拉 " %self.name def pee(self): print "%s 撒 " %self.name class Cat(Animal): def __init__(self, name): self.name = name self.breed = '猫' def cry(self): print '喵喵叫' class Dog(Animal): def __init__(self, name): self.name = name self.breed = '狗' def cry(self): print '汪汪叫' # ######### 执行 ######### c1 = Cat('小白家的小黑猫') c1.eat() c2 = Cat('小黑的小白猫') c2.drink() d1 = Dog('胖子家的小瘦狗') d1.eat() 代码实例
继承重写
class AddrBookEntry(object): 'address book entry class' def __init__(self,nm,ph): self.name = nm self.ph = ph print("Created instance for:",self.name) def UpdatePhone(self,newph): self.ph = newph print('Updated phone %s for:'%self.ph ,self.name) class EmplAddrBookEntry(AddrBookEntry): 'Employee Address Book Entry class' def __init__(self,nm,ph,id,em): #重写init super(EmplAddrBookEntry,self).__init__(nm,ph) #继承父类的init #AddrBookEntry.__init__(self,nm,ph) self.empid = id self.email = em def updateEmail(self,newem): self.email = newem print('Updated e-mail address %s for:'%self.email,self.name) new = AddrBookEntry("koka",'10010') #父类生成实例 new.UpdatePhone("10000") #使用方法更新 newnew = EmplAddrBookEntry("akok","10086","001","wlgc.com") #子类生成实例 继承父类的nm,ph 重写id,em newnew.updateEmail("12345@qq.com") #子类实例使用自己的方法 newnew.UpdatePhone("10010") #子类实例继承使用父类方法
多重继承
import time class A(object): n = 'A' def f2(self): print("f2 from A") def f1(self): print("f1 from A") class B(A): n = 'B' def f1(self): print("f1 from B") #def f2(self): # print("f2 from B") class C(A): n = 'C' def f1(self): print("f1 from C") def f2(self): print("f2 from C") class D(B,C): n = 'D' def __del__(self): #会在程序结束后执行,释放内存 print("deleting the ...") d = D() d.f1() #输出 f1 from B d.f2() #输出 f2 from C # 新式类:D -> B -> C -> A 广度查找(从左至右查找最近的) # 经典类: D-> B -> A 深度查找,在python3.0以后默认为广度查找
多态
实例:
class Animal: def __init__(self, name): # Constructor of the class self.name = name def talk(self): # Abstract method, defined by convention only raise NotImplementedError("Subclass must implement abstract method") class Cat(Animal): def talk(self): return 'Meow!' class Dog(Animal): def talk(self): return 'Woof! Woof!' animals = [Cat('Missy'), Dog('Lassie')] for animal in animals: print animal.name + ': ' + animal.talk()
"""
def animal_talk(obj):
print(obj.talk())
d = Dog("sanjiangyuan")
c = Cat("sanjiangshui")
animal_talk(c)
animal_talk(d)
"""