您的位置:首页 > 编程语言 > Python开发

Python学习(26)--面向对象编程3

2018-03-07 10:53 281 查看
          这一节我们来继续介绍面向对象编程,主要介绍的内容为多继承,多态以及对象属性和类属性的区别或联系,为对象添加属性和方法。
1.多继承
       Python语言与其他高级编程语言的一个重要的区别就是,Python中类的继承支持多继承,其他高级语言如java只支持单继承。所谓多继承,就是一个子类可以继承多个父类,并同时继承所有父类的属性的方法。如下为一个多继承的例子,Father类和Mother类为父类,Child类为子类,类图如下:
       


       如上类图所示,Father类的属性和方法分别为money和run()、smile()。Mother类的属性和方法分别为faceValue和run()、smile()。子类Child继承父类Father和Mother。
      父类Father的代码如下:
class Father():
def __init__(self,money):
self.money=money
def run(self):
print("running")
def smile(self):
print("Father's smile")

       父类Mother的代码如下:
class Mother():
def __init__(self,faceValue):
self.faceValue=faceValue
def eat(self):
print("eating")
def smile(self):
print("Mother's smile")

       子类Child及测试的代码如下:

class Child(Father,Mother):
def __init__(self,money,faceValue):
Father.__init__(self,money)#初始化父类Father的属性money
Mother.__init__(self,faceValue)#初始化父类Mother的属性faceValue
#测试代码部分
child=Child(100,1000)
child.run()#调用父类Father的run()方法
child.eat()#调用父类Mother的eat()方法
child.smile()#调用父类Mother和Mother共同拥有的方法smile()


      代码打印结果如下:



       如上,子类Child对象调用两个父类的方法run()和eat(),多继承中,子类继承所有父类的属性和方法。但当调用两个父类共同拥有的方法smile()时,通过打印结果"Father's smile"可知,此时调用的是父类的方法smile()。原因是当父类列表中的父类们拥有重名方法时,位于父类列表中越靠前位置的父类,其重名方法的优先级别越高,优先级别最高的重名方法将会被调用。
       如上在父类列表中,Father类位于Mother类之前,Father类中smile()方法的优先级别比Mother类中方法smile()的优先级别高,所以子类对象在调用重名方法smile()时,会调用Father类中的smile()方法。
2.多态
       上一节我们介绍过,继承是多态的前提。多态的实现离不开继承,多态字面上的意思就是"多种状态"。在面向对象中,多态的含义为一种功能的多种实现。多态的具体实现分为以下几个步骤:
①子类在继承父类后,覆盖从父类继承的不符合子类功能的方法F。
②覆盖的具体方式为:定义与F重名并且参数列表相同的子类方法F',具体的实现符合子类具体的功能要求。
③使用多态时,具体的子类对象会调用对应覆盖后的子类方法F'。
      如下,我们举一个人给动物喂食的例子来理解多态的概念。首先,我们不使用继承实现多态来解决此问题。例中主要有3个类,Person类,Cat类以及Mouse类,类与类之间的关系如以下类图:



如上类图所示,类Cat和类Mouse的属性为name,方法为eat()。Person类依赖于Cat类和Mouse类,包含的方法为feedCat()和feedMouse()。
Cat类代码如下:
class Cat():
def __init__(self,name):
Animal.__init__(self,name)
def eat(self,food):
print("Cat:"+self.name+" is eating "+food)
Mouse类的代码如下:class Mouse():
def __init__(self,name):
Animal.__init__(self,name)
def eat(self,food):
print("Mouse:"+self.name + " is eating "+food)Person类及测试的代码如下:
from mouse import Mouse
from cat import Cat
class Person():
def feedCat(self,cat,food):
cat.eat(food)#调用Cat类对象的eat()方法
def feedMouse(self,mouse,food):
mouse.eat(food)#调用Mouse类对象的eat()方法
#测试代码部分
person = Person()
cat = Cat("tom")
mouse = Mouse("jerry")
person.feedCat(cat, "fish")
person.feedMouse(mouse, "cheese")
       如上代码,Mouse类和Cat类分别实现各自的eat()方法,在Person类的feedCat()方法中调用Cat对象的eat()方法,实现Person喂食Cat;在Person类的feedMouse()方法中调用Mouse对象的eat()方法,实现Person喂食Mouse。代码打印结果如下:


       这样做有一个很大的缺陷就是不利于后期的维护,例如当有其他100种动物需要喂食时,需要写100个动物类,还需要在Person类中添加100种喂食动物的方法,代码的维护成本很高,解决这个问题就需要用到多态的思想。
       继承是多态的前提,使用多态的思想来解决人喂食动物的问题,需要将动物的共性抽象为一个父类Animal。如下为多态解决此问题时的类图:



如上一共有4个类,Cat类和Mouse类继承自Animal类,Animal类的属性为name,方法为
eat();Person类依赖于Animal类,方法为feedAnimal()。
Cat类的代码如下:
from Animal import Animal
class Cat(Animal):
def __init__(self,name):
Animal.__init__(self,name)
def eat(self,food):
print("Cat:"+self.name+" is eating "+food)
Mouse类的代码如下:
from Animal import Animal
class Mouse(Animal):
def __init__(self,name):
Animal.__init__(self,name)
def eat(self,food):
print("Mouse:"+self.name + " is eating "+food)

Animal类的代码如下:class Animal():
def __init__(self,name):
self.name=name
def eat(self,food):
pass

Person类及测试代码如下:from mouse import Mouse
from cat import Cat
class Person():
def feedAnimal(self,animal,food):
animal.eat(food)
#测试代码如下
person = Person()
cat = Cat("tom")
mouse = Mouse("jerry")
person.feedAnimal(cat,"fish")
person.feedAnimal(mouse,"cheese")
       如上使用多态的思想,将所有动物的共性抽象为父类Animal,子类Cat和Mouse覆盖父类的eat()方法。Person类的feedAnimal(animal,food)方法中调用动物的eat()方法时,具体的子类对象会调用对应子类的的覆盖后的eat()方法。
      例如当调用feedAnimal(animal,food),传入的参数animal是一个Cat类对象时,调用eat()方法会调用Cat类中的覆盖后的eat()方法,而不会调用父类Animal中的eat()方法。
      测试代码打印结果如下:



      使用多态解决人喂食动物的问题,当有其他100种动物需要喂食时,100种动物对应的类只需要继承父类Animal,然后在子类中覆盖父类的eat()方法,而Person类也不需要再像之前那样为每种动物提供喂食的方法,大大减少了后期代码维护的工作量。
      在工程运用中,我们会经常使用到多态的编程思想,熟练的掌握并学会运用它,不仅会大大减少代码的工作量,还会使工程的可维护性提高。
3.对象属性和类属性
       类属性就是在类的内部,构造函数外部定义的属性。访问类属性的方式为:类名.属性名。而对象属性一般是在构造函数内部定义的属性。如下:
class Person():
name="zhangsan"#类属性name
print(Person.name)
person=Person()
print(person.name)
代码打印的结果如下:



     如上,在Person类中,构造函数的外部定义了一个类属性name,并访问它。当创建Person类的对象person,使用person对象访问name时,打印结果和使用Person类直接访问name的打印结果相同。这是因为对象属性比类属性的优先级高,没有对象属性name,则会访问类属性name。这也提醒我们在编程过程中对象属性和类属性的命名不要重名,以免引起不必要的麻烦。
class Person():
name="zhangsan"#类属性name
person=Person()
person.name="lisi"
print(person.name)
print(Person.name)
代码打印结果如下:



      如上代码,创建Person类的对象person,并修改person的属性name值为"lisi"。通过打印结果可以发现,修改后,通过类Person直接访问类属性name,name的值并没有修改。这是因为通过对象修改类属性name时,Python会为当前对象创建一个对象属性name,所以修改的只是当前对象的对象属性name的值,并不是类属性name的值。并且临时为当前对象创建的对象属性只属于当前对象,不属于其他任何对象。如下:

class Person():
name="zhangsan"#类属性name
person1=Person()
person.age=20
print(person.age)
person2=Person()
print(person2.age)
       代码打印结果如下:



      如上代码,修改对象person1的age属性为20,但Person类中并没有定义age属性。这时会为person1创建对象属性age,但对象属性age只属于person1,不属于其他任何对象。当使用person2访问属性name时,打印结果为:" 'Person' object has no attribute 'age' ";对象person2并没有属性age,从而使以上所述得到证明。
      定义一个对象属性,一般在构造函数中定义即可。代码如下:
class Person():
name="zhangsan"#类属性name
def __init__(self,name):
self.name=name#对象属性name
person=Person("lisi")
print(person.name)
print(Person.name)
      代码打印结果如下:



      如上代码,定义了一个类属性name,在构造函数内部也定义了对象属性name,创建对象person并访问属性name,打印结果为"lisi"。而通过Person类直接访问属性name时打印结果为"zhangsan"。这证明对象属性的优先级别比类属性高,对象属性与类属性重名时,对象优先访问对象属性;若没有与类属性重名的对象属性,对象可以直接访问类属性。
4.动态的为对象添加属性和方法
      Python作为一门动态的语言,允许程序动态的为一个自定义对象添加属性和方法。但所添加的属性和方法只属于当前对象,不属于其他的任何对象。为对象添加属性在之前的讲解中其实介绍到过。如下代码:
class Person():
pass
person=Person()
person.name="zhangsan"#为person对象添加对象属性name
print(person.name)
del person.name#删除person对象的属性name
print(person.name)
          打印结果如下:


   为对象添加属性的方法为:对象名.属性名=属性值。如person.name="zhangsan",Person类并没有为person对象定义对象属性name,这时会为person对象添加一个只属于它的对象属性name。删除对象属性的方式为:del 对象名.属性名,删除后当前对象不再具有此属性。如del person.name,删除person对象的属性name,再次访问时,person不再具有属性name,所以打印结果为:" 'Person' object has no attribute 'age' "。
        如下为为自定义对象添加方法的例子:
from types import MethodType #使用模块types的MethodType()方法为对象添加方法
class Person():
def __init__(self,name):
self.name=name
def say(self): #定义为对象添加的方法say()
print("my name is "+self.name)
person=Person("zhangsan")
person.speak=MethodType(say,person) #为person对象添加方法speak(),
# 方法speak()具体的实现为方法say(),当前对象self=person
person.speak()
del person.speak #删除对象person的方法speak()
person.speak()       代码打印结果为:



          
        如上代码,为一个对象添加方法需要使用到types模块的方法MethodType(),为对象添加方法的方式为:对象名.方法名=MethodType(method,object),方法MethodType()的第一个参数method为所添加方法的具体实现,第二个参数object为当前对象。
  如:person.speak=MethodType(say,person),为person对象添加方法speak(),speak()的具体实现为方法say(),object为当前对象person,当前对象为被添加方法的对象。删除对象方法的方式为:del 对象名.方法名。如上代码:del person.speak,删除对象person的方法speak()后,再次调用方法speak(),打印结果为:'Person' object has no attribute 'speak'。
      最后请再次注意,动态的为对象添加方法和属性,只是为当前一个对象添加对象和方法,与其他对象毫无关系。
      以上就是本节的内容,下一节我们将继续介绍面向对象编程,敬请期待。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息