比尔云BierYun--阿里云最新优惠活动
阿里云优惠码丨阿里云代金券

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

Python学习(26)–面向对象编程3http://www.bieryun.com/2386.html

这一节我们来继续介绍面向对象编程,主要介绍的内容为多继承,多态以及对象属性和类属性的区别或联系,为对象添加属性和方法。

1.多继承

Python语言与其他高级编程语言的一个重要的区别就是,Python中类的继承支持多继承,其他高级语言如java只支持单继承。所谓多继承,就是一个子类可以继承多个父类,并同时继承所有父类的属性的方法。如下为一个多继承的例子,Father类和Mother类为父类,Child类为子类,类图如下:

如上类图所示,Father类的属性和方法分别为money和run()、smile()。Mother类的属性和方法分别为faceValue和run()、smile()。子类Child继承父类Father和Mother。

父类Father的代码如下:

[python] view plain copy

  1. class Father():
  2.     def __init__(self,money):
  3.         self.money=money
  4.     def run(self):
  5.         print(“running”)
  6.     def smile(self):
  7.         print(“Father’s smile”)

 

父类Mother的代码如下:

[python] view plain copy

  1. class Mother():
  2.     def __init__(self,faceValue):
  3.         self.faceValue=faceValue
  4.     def eat(self):
  5.         print(“eating”)
  6.     def smile(self):
  7.         print(“Mother’s smile”)

 

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

[python] view plain copy

  1. class Child(Father,Mother):
  2.     def __init__(self,money,faceValue):
  3.         Father.__init__(self,money)#初始化父类Father的属性money
  4.         Mother.__init__(self,faceValue)#初始化父类Mother的属性faceValue

[python] view plain copy

  1. #测试代码部分
  2. child=Child(100,1000)
  3. child.run()#调用父类Father的run()方法
  4. child.eat()#调用父类Mother的eat()方法
  5. 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类代码如下:

[python] view plain copy

  1. class Cat():
  2.     def __init__(self,name):
  3.         Animal.__init__(self,name)
  4.     def eat(self,food):
  5.         print(“Cat:”+self.name+” is eating “+food)

Mouse类的代码如下:

[python] view plain copy

  1. class Mouse():
  2.     def __init__(self,name):
  3.         Animal.__init__(self,name)
  4.     def eat(self,food):
  5.         print(“Mouse:”+self.name + ” is eating “+food)

Person类及测试的代码如下:

[python] view plain copy

  1. from mouse import Mouse
  2. from cat import Cat
  3. class Person():
  4.     def feedCat(self,cat,food):
  5.         cat.eat(food)#调用Cat类对象的eat()方法
  6.     def feedMouse(self,mouse,food):
  7.         mouse.eat(food)#调用Mouse类对象的eat()方法

[python] view plain copy

  1. #测试代码部分
  2. person = Person()
  3. cat = Cat(“tom”)
  4. mouse = Mouse(“jerry”)
  5. person.feedCat(cat, “fish”)
  6. 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类的代码如下:

[python] view plain copy

  1. from Animal import Animal
  2. class Cat(Animal):
  3.     def __init__(self,name):
  4.         Animal.__init__(self,name)
  5.     def eat(self,food):
  6.         print(“Cat:”+self.name+” is eating “+food)

 

Mouse类的代码如下:

[python] view plain copy

  1. from Animal import Animal
  2. class Mouse(Animal):
  3.     def __init__(self,name):
  4.         Animal.__init__(self,name)
  5.     def eat(self,food):
  6.         print(“Mouse:”+self.name + ” is eating “+food)

Animal类的代码如下:

[python] view plain copy

  1. class Animal():
  2.     def __init__(self,name):
  3.         self.name=name
  4.     def eat(self,food):
  5.         pass

Person类及测试代码如下:

[python] view plain copy

  1. from mouse import Mouse
  2. from cat import Cat
  3. class Person():
  4.     def feedAnimal(self,animal,food):
  5.         animal.eat(food)
  6. #测试代码如下
  7. person = Person()
  8. cat = Cat(“tom”)
  9. mouse = Mouse(“jerry”)
  10. person.feedAnimal(cat,“fish”)
  11. 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.对象属性和类属性

类属性就是在类的内部,构造函数外部定义的属性。访问类属性的方式为:类名.属性名。而对象属性一般是在构造函数内部定义的属性。如下:

[python] view plain copy

  1. class Person():
  2.     name=“zhangsan”#类属性name
  3. print(Person.name)
  4. person=Person()
  5. print(person.name)

 

代码打印的结果如下:

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

[python] view plain copy

  1. class Person():
  2.     name=“zhangsan”#类属性name
  3. person=Person()
  4. person.name=“lisi”
  5. print(person.name)
  6. print(Person.name)

 

代码打印结果如下:

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

[python] view plain copy

  1. class Person():
  2.     name=“zhangsan”#类属性name
  3. person1=Person()
  4. person.age=20
  5. print(person.age)
  6. person2=Person()
  7. print(person2.age)

 

代码打印结果如下:

如上代码,修改对象person1的age属性为20,但Person类中并没有定义age属性。这时会为person1创建对象属性age,但对象属性age只属于person1,不属于其他任何对象。当使用person2访问属性name时,打印结果为:” ‘Person’ object has no attribute ‘age’ “;对象person2并没有属性age,从而使以上所述得到证明。

定义一个对象属性,一般在构造函数中定义即可。代码如下:

[python] view plain copy

  1. class Person():
  2.     name=“zhangsan”#类属性name
  3.     def __init__(self,name):
  4.         self.name=name#对象属性name
  5. person=Person(“lisi”)
  6. print(person.name)
  7. print(Person.name)

 

代码打印结果如下:

如上代码,定义了一个类属性name,在构造函数内部也定义了对象属性name,创建对象person并访问属性name,打印结果为”lisi”。而通过Person类直接访问属性name时打印结果为”zhangsan”。这证明对象属性的优先级别比类属性高,对象属性与类属性重名时,对象优先访问对象属性;若没有与类属性重名的对象属性,对象可以直接访问类属性。

4.动态的为对象添加属性和方法

Python作为一门动态的语言,允许程序动态的为一个自定义对象添加属性和方法。但所添加的属性和方法只属于当前对象,不属于其他的任何对象。为对象添加属性在之前的讲解中其实介绍到过。如下代码:

[python] view plain copy

  1. class Person():
  2.     pass
  3. person=Person()
  4. person.name=“zhangsan”#为person对象添加对象属性name
  5. print(person.name)
  6. del person.name#删除person对象的属性name
  7. 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’ “。

如下为为自定义对象添加方法的例子:

[python] view plain copy

  1. from types import MethodType #使用模块types的MethodType()方法为对象添加方法
  2. class Person():
  3.     def __init__(self,name):
  4.         self.name=name
  5. def say(self):               #定义为对象添加的方法say()
  6.     print(“my name is “+self.name)
  7. person=Person(“zhangsan”)
  8. person.speak=MethodType(say,person)    #为person对象添加方法speak(),
  9.       # 方法speak()具体的实现为方法say(),当前对象self=person
  10. person.speak()
  11. del person.speak  #删除对象person的方法speak()
  12. 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’。

最后请再次注意,动态的为对象添加方法和属性,只是为当前一个对象添加对象和方法,与其他对象毫无关系。

以上就是本节的内容,下一节我们将继续介绍面向对象编程,敬请期待。

未经允许不得转载:比尔云 » Python学习(26)–面向对象编程3
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

强烈推荐

高性能SSD云服务器ECS抗攻击,高可用云数据库RDS