多态性是面向对象的三大特性之一。当同一操作用于不同的对象,可以有不同的解释,产生不同的执行结果,这种特性称为多态性。在计算机语言中的多态性,一般是指调用一个同名函数,参数不同,会产生不同的执行结果。多态性可以是静态的或动态的。静态多态性,是有若干同名函数,函数的形参类型和个数不同,系统在编译时,根据调用方法的实参类型及实参的个数决定调用哪个同名方法,实现何种操作。动态多态性,是调用一个名字相同,形参的类型及个数完全一样的方法,在程序运行时根据实参的不同,完成不同的操作。C#运行时的多态性通过虚方法实现,复杂难懂。python函数的形参没有类型,仅仅是一个名字,当形参被实参赋值,形参将引用(代表)实参,函数就可以操作实参所引用的类对象,此概念将极大简化动态多态性的实现。由于python不用编译,是解释执行的,这里只讨论动态多态性。下面的例子说明用C#语言实现动态多态性的方法。
using System;
class A
{ public void F()
{ Console.Write(" A.F"); }
public virtual void G()
{ Console.Write(" A.G"); }
}
class B:A
{ new public void F()
{ Console.Write(" B.F"); }
public override void G()
{ Console.Write(" B.G"); }
}
class Test
{ static void F2(A aA)
{ aA.G(); }
static void Main()
{ B b=new B();
A a1=new A();
A a2=b;
a1.F();
a2.F();
b.F();
a1.G();
a2.G();
F2(a2);
F2(a1);
}
}
那么输出应该是:A.F A.F B.F A.G B.G B.G A.G 这里实现的要点是函数F2()的形参类型是类A,那么实参必须是A类对象或A类的派生类的对象,由于方法G()是虚方法,实参是A类对象,调用A类方法G(),实参是B类对象,调用B类方法G()。从而完成不同功能。这里涉及C#语言众多概念,十分复杂。 用Python实现相同功能,就简单多了,例子如下。
class A:
def G(self):
print(" A.G")
class B(A):
def G(self):
print(" B.G")
def F2(o):
o.G()
aA=A()
aB=B()
F2(aA)
F2(aB)
运行后,将显示: A.G B.G 实际上,实现动态多态性并不要求两个类存在继承关系,即上例并不要求B类是A类子类。如果B类定义中,第4条语句变为被注释的第5条语句,仍能正常工作,但要求两个类中被函数F2()调用的方法名称必须相同。 python多态性的实现是利用了变量是对类对象的引用(代表)的规则。这里解释这个规则,变量有两部分:字符组成的变量名和保存数据的内存单元地址,令a=2,其实是将整数2保存到内存数据区,在内存变量区先保存变量名a,在其后保存整数2所在内存的地址。因此必须对变量赋值,变量才可以使用。用变量访问数据,实际是通过变量名得到地址,用地址访问数据,这种方式被称作变量a引用(代表)了整数2。如果再令a=2.3,先将浮点数2.3保存到内存数据区,变量名保持不变,仅将其后的地址换为浮点数所在内存的地址,称作变量a引用(代表)了浮点数2.3,由于变量a不再引用整数2,如果也没有其它变量引用整数2,整数2所占用的内存单元将被垃圾收集器收回。函数F2()的形参仅仅有一个名字,还未保存对应数据的地址,应是None。当程序运行后,函数F2()将被调用,形参被实参赋值。实参一定是引用了某个类对象,那么形参也将和实参一样都引用了这个类对象,即两者都引用了同一个类对象,那么通过形参就可以调用这个类对象的函数,完成指定功能。 动态多态性概念在python中被广泛使用,很多内置函数都采用python动态多态性的概念。例如python内置函数len()能检测多种数据类型的长度,也是采用了动态多态性的概念,实现步骤和上例类似。python所有数据类型都是类,包括字符串类、列表类和元组类等,这些类中都应有一个同名的检测自己长度的方法,该方法返回值为所得到的长度,假如都为G()。内置函数len()实参必定引用(代表)某一个数据类对象,将实参赋值给形参,形参将也引用(代表)这一个数据类对象,通过形参可以调用这个数据类的方法G(),将得到该数据类对象的长度返回。如果对象是字符串,则返回字符数,如果对象是列表,则返回列表中的项目数等。
|