python中的多态

一些想法

Posted by UlyC on May 15, 2018

Science is more art than scien, Morty.——Rick
用科学怎么能理解科学这门艺术。

我的博客

对于pyhton中多态的思考

在《父与子编程》中,多态的定义是:

对于不同的类,可以有同名的两个(或多个)方法。

但是没有提到继承,这两个不同的类可以是不相关类,并举了一个例子:

class Triangle:  

		def __init__(self, width, height):  
		 	self.width = width  
			self.height = height  

		def getArea(self):  
			area = self.width * self.height / 2.0  
		    return area
class Square:   
 		def __init__(self, size):        
			self.size = size  

		def getArea(self):  
      		area = self.size * self.size         
			return area 

并在接下来说道:

这两个形状都使用了getArea()方法,不过每个形状中这个方法做的工作不一样, 这就是一个多态的例子。

这样就使人陷入了迷惑,首先Square和Triangle是两个不相关类,没有相同继承关系。

而且本书中使用的是python2,python2中如果没有写明是新式类那么就是默认是经典类,经典类并不继承自object类,故Square和Triangle也不是同一个父类的不同子类,完全是不相干的类。

从“多态就是同一种事物的多种形态”这一概念来说,面积可以是矩形的面积,也可以是三角形的面积,确实是多态。

但是在java中有多态的三要素:

  1. 继承的存在。
  2. 子类要重写父类的方法(多态下调用子类重写后的方法)
  3. 父类引用变量指向子类对象(子类到父类的类型转换)

据此来说,《父与子编程》中的例子根本就不是多态了。 现在不确定的是这三要素在python中是否适用。 并由此我有了以下一些疑问(对于python):

  1. 继承是多态的充要条件吗?如果是,上面的例子该怎么解释?

  2. 如果“多态就是同一种事物的多种形态”作为python中多态的概念,那么:
    • 同一父类的两个子类,具有父类没有的同名方法,算不算多态?
    • 两个不相关类,具有同名方法,算不算多态?
  3. java实现多态的三要素严格适用于python吗?

  4. 如果只存在子类对父类方法的重写,而没有父类变量引用子类变量还算多态吗?

  5. 同一个子类的不同子类实例对象使用子类重写的方法,还属于多态吗?

  6. 函数:
    def func(a):
         print(a)
    

    传入不同类型的参数a,得到不同实现结果,属不属于多态的一种表现?

  7. 多态和多态性有区别吗?

python中多态的定多态和多态性的区别在哪里?

要解决以上的疑问,必须先弄清楚python中多态的定义。不同书中对于pyhton里多态的说法不尽相同, 且往往为了便于理解只举例子不说概念,还有些干脆直接说python中无多态(从python变量本身无类型的角度)。

wiki中对于多态的定义是:

多态(polymorphism)是指计算机程序运行时,相同的消息可能会送给多个不同类别之对象,而系统可依据对象所属类别,引发对应类别的方法,而有不同的行为。

《pyhton学习手册(第四版)》:

在X.method方法中,method的意义取决于X的类型。

《父与子编程》:

对于不同的类,可以有同名的两个(或多个)方法,就是多态。

还有一个许多地方提到的,比较贴近自然语言的说法:

多态是同一种事物的多种形态。

似乎都没有提到继承这一前提。

我们来看看廖雪峰的博客是怎么写的,他并没有直接给多态下定义,而是引入了静态语言语言和动态语言的鸭子类型 的概念来讲解:

有了继承,才能有多态。在调用类实例方法的时候,尽量把变量视作父类类型,这样所有子类都可以正常被接收。

动态语言的鸭子类型特点决定了继承不像静态语言那样是必须的。

这句话并没有特别说是多态,我理解的是说“有了继承,才能有多态”是对于静态语言而言,python中的多态不像静态语言那样必须需要继承。

结合这篇文章python面向对象编程的继承,多态与多态性,封装

多态是(从定义角度出发):同一种事物的多种形态。概念非常广,反映在不同编程语言中有不同的体现。

多态性是(从使用角度出发):同一种调用方式,不同调用效果,具有不同功能的函数可以使用同一个函数名。是多态在不同编程语言中的一种体现,即实现。

搞清楚了概念,于是以上的疑问可以迎刃而解。

问题解答

1. 继承是多态的必要条件吗?如果是,上面的例子该怎么解释?

对于java等静态语言来说, 继承是多态(实现)的充要条件。 如下代码:

Class A{
	public void func(){
		System.out.println("A.func")
	}
}

Class B{
	public void func(){
		System.out.println("B.func")
	}
}

Class Test{
	public static void main(String[] args){
		A tmp =new A();
		tmp.func();
		
		B tmp =new B();
		tmp.func();
	}
}

两个不相关类A和B。

变量tmp有两个不相关类型,A类型和B类型,而两个类中func方法虽然同名,打印出来也得到不同结果,但是完全不同,属于两种独立事物,不属于“同种事物的多种形态”。 严格的多态定义一定依赖于继承。

而对于pyhton这种动态语言来说,并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子,那它就可以被看做是鸭子”。意即对于两个不同类(不一定相关)里的同名方法,可以近似看做同一事物。这个角度来说python是一种多态语言,处处体现着多态。

可是“看做鸭子”和真“鸭子”毕竟不同,python里就不存在对“同一种”方法的调用,调用的只是一个一个可以看做同一种的方法,这个角度说python中是不存在多态的,我们说的python中的多态只是对多态的一种模拟。

2. 如果“多态就是同一种事物的多种形态”作为python中多态的概念,那么:

  • 同一父类的两个子类,具有父类没有的同名方法,算不算多态?
  • 两个不相关类,具有同名方法,算不算多态?

通过前述可知,对于多态在不同语言中有不同的表现,在python中这两种都属于多态的表现,而在java等静态语言中则不属于,具体可见前一问的例子。

3. java实现多态的三要素严格适用于python吗?

并不严格适用,pyhton并不要求严格的继承体系。 多态三要素只是多态在java语言中表现出的多态性。

4.如果只存在子类对父类方法的重写,而没有父类变量引用子类变量还算多态吗?

由定义可知,多态需要被方法被调用才能体现,可以说是一种多态,但是没有表现出多态性。

5.同一个子类的不同子类实例对象使用子类重写的方法,还属于多态吗?

只有同种方法得到同种调用,输出不同结果才行。 子类对象使用子类重写的方法,只有一种调用,得到同种输出。

6. 函数:

def func(a):
		print(a)

传入不同类型的参数a,得到不同实现结果,属不属于多态的一种表现?

在其他语言中不算,在python中可以看做是多态。

我对这Python里这个函数的理解是这样的:

因为变量a在python中可以传入类型的参数或者说对象。 a 暂且认为隐含了 “a = object()”这一条件,是一种父类变量,不管传入的是数字类型或者字符串等类型,都可以视作object的一个子类实例化对象,print会调用各种对象中的__ str__魔法方法,这也实现了“父类变量引用子类对象”,即多态。 (pyhton中万物皆对象,java中基本数据类型不是对象) 这么理解很不严谨,但也帮我理解了python为什么是一种多态语言。

7.多态和多态性有区别吗?

有,但是平时说的时候其实是放在一起说的。

多态是(从定义角度出发):同一种事物的多种形态。概念非常广,反映在不同编程语言中有不同的体现。

多态性是(从使用角度出发):同一种调用方式,不同调用效果,具有不同功能的函数可以使用同一个函数名。是多态在不同编程语言中的一种体现,即实现。


知识共享许可协议
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。