Python系列之 - 面向对象(2)

类的三大特性 
类的三大特性包括: 
封装、继承、多态

一 封装 
封装就是将类所用到的所有字段、属性、方法都包含在类代码段里面,当实例调用直接调用类中的方法即可。

class People(object):

    def __init__(self, name):
        self.name = name

    def talk(self):
        print("my name is {0}".format(self.name))

 

将函数talk() 封装到了类People下面,当实例化调用此函数时,只需要实例名+方法名即可调用

zhangsan = People("zhangsan")
zhangsan.talk()
结果:
my name is zhangsan

 

对于类中的一些方法,如果我们在初始化(构造函数)中需要调用时,可以写成私有方法。这样在实例化对象后就直接执行。

class People(object):
    color = 'red'

    def __init__(self, name):
        self.name = name
        self.__talk()

    def __talk(self):
        print("my name is {0}, color is {1}".format(self.name, self.color))

  zhangsan = People("zhangsan")
  结果:
  my name is zhangsan,color is red
  不用再调用talk方法

 

继承 
继承是子类可以拥有父类的方法及属性、字段(对象字段), 但不可以继承类的私有方法(__开头的方法). 
对于父类中有的方法,子类可以直接使用也可以重写。

class People(object):
    color = 'red'

    def __init__(self, name):
        self.name = name
        self.__talk()

    def __talk(self):
        print("my name is {0}, color is {1}".format(self.name, self.color))
    def show_job(self):
        print(" No job")

class Teacher(People):
    def __init__(self, name, age):
        super(Teacher, self).__init__(name)
        self.age = age
        self.__talk()

    def __talk(self):
        print("I'm Tearcher,  I'm {0}".format(self.age))
    def show_job(self):
        print(" Teacher")

zhangsan = Teacher("zhangsan",20)
zhangsan.show_job()
结果:
my name is zhangsan, color is red
I'm Tearcher,  I'm 20
Teacher

 

如上,虽然People与Teacher均有__talk方法,但该方法为私有方法,子类Teacher是无法继承的,而且show_job方法 虽然父类People中也有,但子类进行了重写。这样实例化调用时,仍然调动子类方法。

继承顺序: 
经典类: 深度优先 
新式类: 广度优先 
在python2.x中对于多继承存在一个bug,对于新式类和经典类的多继承有以上区分,在python3.x中已经修复了,均为广度优先。

经典类:

class Father():
    def show(self):
        print("show in Father")

class Son_1(Father):
    def __init__(self):
        pass

    #def show(self):
    #    print("show in Son_1")

class Son_2(Father):
    def show(self):
        print("show in Son_2")

class subSon(Son_1,Son_2):
    def __init__(self):
        pass

p = subSon()
p.show()
结果:
show in Father
新式类:

class Father(object):
    def show(self):
        print("show in Father")

class Son_1(Father):
    def __init__(self):
        pass

    #def show(self):
    #    print("show in Son_1")

class Son_2(Father):
    def show(self):
        print("show in Son_2")

class subSon(Son_1,Son_2):
    def __init__(self):
        pass

p = subSon()
p.show()
结果:
show in Son_2

 

以上是在python2中的执行结果范例 
深度优先: 先去Son_1中找,如果找不到就去Son_1的父类Father中找,如果找到就执行,找不到就到Son_2中找 
广度优先:先去Son_1中找,如果找不到就去兄弟节点Son_2中找,都没有就去父类Father中找。

多态 
什么是多态,简而言之就是对于同一个方法或函数,根据不同的输入能获取不同的结果的表现形式,多态性意味着有多重形式。在面向对象编程范式中,多态性往往表现为”一个接口,多个功能”。 
我们通过以下两个例子来理解以下多态(以下代码为C#中的代码) 
1 函数的重载方式多态

using System;
namespace PolymorphismApplication
{
   class Printdata
   {
      void print(int i)
      {
         Console.WriteLine("Printing int: {0}", i );
      }

      void print(double f)
      {
         Console.WriteLine("Printing float: {0}" , f);
      }

      void print(string s)
      {
         Console.WriteLine("Printing string: {0}", s);
      }
      static void Main(string[] args)
      {
         Printdata p = new Printdata();
         // 调用 print 来打印整数
         p.print(5);
         // 调用 print 来打印浮点数
         p.print(500.263);
         // 调用 print 来打印字符串
         p.print("Hello C++");
         Console.ReadKey();
      }
   }
}

 

我们通过以上看到,同一个函数名print(),由于传入的值不一样,得到的结果也不一样(这不是关键), 关键在于在一个模块中可以对用同一个函数名来定义多种方式,也就是一个接口的多种表现形式。

2 再来看一个关于类的例子:


在Caller模块中,对CallArea()方法传递的对象不同,获取的值也不一样,而且对传入的值类型必须是Shape 的类对象。传入别的值就会报错。

从以上2个例子可以看到,C#是属于强类型语言,所有函数或类方法中定义时就必须指定数据类型,这样表现出来的就是一个函数(接口)可以有不同的功能。这就属于多态。

而python是一门弱类型语言,对于所有函数或类方法只有在调用时才知道数据的类型。它是不支持多态的,也不需要使用多态。如果要实现多态的效果,可以参考一下方class F1    pass

class S1(F1):

    def show(self):
        print 'S1.show'


class S2(F1):

    def show(self):
        print 'S2.show'

 

using System;
namespace PolymorphismApplication
{
   # 父类shape
   class Shape 
   {
      protected int width, height;
      public Shape( int a=0, int b=0)
      {
         width = a;
         height = b;
      }
      public virtual int area()
      {
         Console.WriteLine("父类的面积:");
         return 0;
      }
   }

   # 子类Rectangle
   class Rectangle: Shape
   {
      public Rectangle( int a=0, int b=0): base(a, b)
      {

      }
      public override int area ()
      {
         Console.WriteLine("Rectangle 类的面积:");
         return (width * height); 
      }
   }
   # 子类 Triangle
   class Triangle: Shape
   {
      public Triangle(int a = 0, int b = 0): base(a, b)
      {

      }
      public override int area()
      {
         Console.WriteLine("Triangle 类的面积:");
         return (width * height / 2); 
      }
   }

    # 另一个类
   class Caller
   {
      # 定义一个方法,传入的值为一个Shape类的对象
      public void CallArea(Shape sh)
      {
         int a;
         a = sh.area();
         Console.WriteLine("面积: {0}", a);
      }
   }  

   class Tester
   {

      static void Main(string[] args)
      {
         # 实例化一个对象
         Caller c = new Caller();
         # 实例化一个Rectangle对象
         Rectangle r = new Rectangle(10, 7);
         # 实例化一个Triangle对象
         Triangle t = new Triangle(10, 5);
         # 调用c对象的CallArea方法,传入Rectangle
         c.CallArea(r);
         c.CallArea(t);
         Console.ReadKey();
      }
   }
}
执行结果:
Rectangle 类的面积:
面积:70
Triangle 类的面积:
面积:25

 


#
由于在Java或C#中定义函数参数时,必须指定参数的类型 # 为了让Func函数既可以执行S1对象的show方法,又可以执行S2对象的show方法,所以,定义了一个S1和S2类的父类 # 而实际传入的参数是:S1对象和S2对象
def Func(F1 obj):
    """Func函数需要接收一个F1类型或者F1子类的类型"""

    print obj.show()

s1_obj = S1()
Func(s1_obj) # 在Func函数中传入S1类的对象 s1_obj,执行 S1 的show方法,结果:S1.show

s2_obj = S2()
Func(s2_obj) # 在Func函数中传入Ss类的对象 ss_obj,执行 Ss 的show方法,结果:S2.show

 



通过这种方式可以实现一种伪多态的效果。
posted @ 2018-04-12 07:47  慕沁  阅读(237)  评论(0编辑  收藏  举报