|
85 | 85 |
|
86 | 86 | 争论,让给思想者。我们,围观。 |
87 | 87 |
|
88 | | -在诸多介绍多态的文章中,都会有这样关于猫和狗的例子。这里也将代码贴出来,读者去体会所谓多态体现。其实,如果你进入了Python的语境,有时候是不经意就已经在应用多态特性呢。 |
| 88 | +为了让读者能够进一步理解Python的多态特点,必须要比较,不跟世界上三分之二尚处于水深火热的劳苦大众比较,怎能体会到自己生活在幸福的祖国大花园内。 |
89 | 89 |
|
90 | | - #!/usr/bin/env python |
91 | | - # coding=utf-8 |
| 90 | +《Thinking in Java》的作者Bruce Eckel在2003年5月2日发表了一篇题为[《Strong Typing vs. Strong Testing》](https://docs.google.com/document/d/1aXs1tpwzPjW9MdsG5dI7clNFyYayFBkcXwRDo-qvbIk/preview)的博客,在其中将Java和Python的多态特征进行了比较,在此我选摘部分内容,重温大师的论述。 |
92 | 91 |
|
93 | | - "the code is from: http://zetcode.com/lang/python/oop/" |
| 92 | +先来欣赏大师所撰写的一段Java代码: |
94 | 93 |
|
95 | | - class Animal(object): #Python 3: class Animal: |
96 | | - def __init__(self, name=""): |
97 | | - self.name = name |
98 | | - |
99 | | - def talk(self): |
100 | | - pass |
| 94 | + // Speaking pets in Java: |
| 95 | + interface Pet { |
| 96 | + void speak(); |
| 97 | + } |
| 98 | + |
| 99 | + class Cat implements Pet { |
| 100 | + public void speak() { System.out.println("meow!"); } |
| 101 | + } |
| 102 | + |
| 103 | + class Dog implements Pet { |
| 104 | + public void speak() { System.out.println("woof!"); } |
| 105 | + } |
| 106 | + |
| 107 | + public class PetSpeak { |
| 108 | + static void command(Pet p) { p.speak(); } |
| 109 | + public static void main(String[] args) { |
| 110 | + Pet[] pets = { new Cat(), new Dog() }; |
| 111 | + for(int i = 0; i < pets.length; i++) |
| 112 | + command(pets[i]); |
| 113 | + } |
| 114 | + } |
| 115 | + |
| 116 | +如果读者没有学习过Java,对上述代码理解可能不是很顺畅,这不重要。主要观察`command(Pet p)`,这种写法意味着函数`command()`所能接受的参数的类型必须是`Pet`类型,其它类型不行。所以,必须创建`interface Pet`这个接口并且类Cat和Dog继承它,然后才能upcast them to the generic command() method。(原文: I must create a hierarchy of Pet, and inherit Dog and Cat so that I can upcast them to the generic command() method.) |
| 117 | + |
| 118 | +与上面的代码相对应,大师提供了Python代码,如下所示: |
| 119 | + |
| 120 | + # Speaking pets in Python: |
| 121 | + class Pet: |
| 122 | + def speak(self): pass |
| 123 | + |
| 124 | + class Cat(Pet): |
| 125 | + def speak(self): |
| 126 | + print "meow!" |
| 127 | + |
| 128 | + class Dog(Pet): |
| 129 | + def speak(self): |
| 130 | + print "woof!" |
| 131 | + |
| 132 | + def command(pet): |
| 133 | + pet.speak() |
| 134 | + |
| 135 | + pets = [ Cat(), Dog() ] |
| 136 | + |
| 137 | + for pet in pets: |
| 138 | + command(pet) |
| 139 | + |
| 140 | +注意这段Python代码中的`command()`函数,其参数`pet`并没有要求必须是前面的`Pet`类型(注意区分大小写),仅仅是一个名字为`pet`的对象引用罢了。Python不关心引用的对象是什么类型,只要改对象有`speak()`方法即可。提醒读者注意的是,因为历史原因(2003年),大师当时写的是旧式类。 |
| 141 | + |
| 142 | +根据我们对Python的理解,上面代码中的类`Pet`其实是多余的。是的,大师也这么认为,只是因为大师当时是完全模仿Java程序而写的。随后,大师就修改了上面的代码。 |
| 143 | + |
| 144 | + # Speaking pets in Python, but without base classes: |
| 145 | + class Cat: |
| 146 | + def speak(self): |
| 147 | + print "meow!" |
| 148 | + |
| 149 | + class Dog: |
| 150 | + def speak(self): |
| 151 | + print "woof!" |
| 152 | + |
| 153 | + class Bob: |
| 154 | + def bow(self): |
| 155 | + print "thank you, thank you!" |
| 156 | + def speak(self): |
| 157 | + print "hello, welcome to the neighborhood!" |
| 158 | + def drive(self): |
| 159 | + print "beep, beep!" |
| 160 | + |
| 161 | + def command(pet): |
| 162 | + pet.speak() |
101 | 163 |
|
102 | | - class Cat(Animal): |
103 | | - def talk(self): |
104 | | - print "Meow!" #Python 3: print('Meow!'),下同,从略 |
| 164 | + pets = [ Cat(), Dog(), Bob() ] |
105 | 165 |
|
106 | | - class Dog(Animal): |
107 | | - def talk(self): |
108 | | - print "Woof!" |
| 166 | + for pet in pets: |
| 167 | + command(pet) |
109 | 168 |
|
110 | | - a = Animal() |
111 | | - a.talk() |
| 169 | +不仅去掉了没什么用的类`Pet`,又增加了一个新的类`Bob`,这个类根本不是如`Cat`和`Dog`那样的类型,只是它碰巧也有一个名字为`speak()`的方法罢了。但是,也依然能够在`command()`函数中被调用。 |
112 | 170 |
|
113 | | - c = Cat("Missy") |
114 | | - c.talk() |
| 171 | +这就是Python中的多态特点,大师Brue Eckel通过非常有说明了的代码说明了Java和Python的区别,并充分展示了Python中的多态特征。 |
115 | 172 |
|
116 | | - d = Dog("Rocky") |
117 | | - d.talk() |
| 173 | +诚如前面所述,Python不检查传入对象的类型(上面大师所写的代码中非常清晰表明了这点),这种方式被称之为“隐式类型”(laten typing)或者“结构式类型”(structural typing),也被通俗的称为“鸭子类型”(duck typeing),其含义在维基百科中被表述为: |
118 | 174 |
|
119 | | -保存后运行之: |
| 175 | +>在程序设计中,鸭子类型(英语:duck typing)是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定。这个概念的名字来源于由James Whitcomb Riley提出的鸭子测试,“鸭子测试”可以这样表述:“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。” |
120 | 176 |
|
121 | | - $ python 21101.py |
122 | | - Meow! |
123 | | - Woof! |
| 177 | +鸭子类型就意味着可以向任何对象发送任何消息,语言只关心该对象能否接受该消息,不强求该对象是否某一种特定的类型——该对象的多态表现。 |
124 | 178 |
|
125 | | -代码中有`Cat`和`Dog`两个类,都继承了类`Animal`,它们都有`talk()`方法,输入不同的动物名称,会得出相应的结果。 |
| 179 | +对于Python的这种特征,有一批程序员不接受,他们认为在程序被执行的时候,可能收到错误的对象,而且这种错误还可能潜伏在程序的某个角落。因此在编程领域就有了“强类型”(如Java)和“弱类型”(如Python)之争。 |
126 | 180 |
|
127 | | -根据前面已经学习过的类和继承的知识,我们知道,对于实例`c`是Cat类型的对象,`d`是Dog类型的对象,但它们也都是Animal类型的对象——类,也是一种对象类型,这样,不论是谁,也都有`Animal`类所规定的方法和属性——多态就体现在这里——继承了`Animal`的子类,实例化之后都可以具有`Animal`的表现形式。 |
| 181 | +对于此类争论,大师Brue Eckel在上面所提到的博客中,给出了非常明确的回答。下面原文恭录于此: |
128 | 182 |
|
129 | | -对“多态”的理解,还可以在实践中慢慢增加。 |
| 183 | +>Strong testing, not strong typing. |
130 | 184 |
|
131 | | -关于多态,有一个被称作“鸭子类型”(duck typeing)的东西,其含义在维基百科中被表述为: |
| 185 | +>So this, I assert, is an aspect of why Python works. C++ tests happen at compile time (with a few minor special cases). Some Java tests happen at compile time (syntax checking), and some happen at run time (array-bounds checking, for example). Most Python tests happen at runtime rather than at compile time, but they do happen, and that's the important thing (not when). And because I can get a Python program up and running in far less time than it takes you to write the equivalent C++/Java/C# program, I can start running the real tests sooner: unit tests, tests of my hypothesis, tests of alternate approaches, etc. And if a Python program has adequate unit tests, it can be as robust as a C++, Java or C# program with adequate unit tests (although the tests in Python will be faster to write). |
132 | 186 |
|
133 | | ->在程序设计中,鸭子类型(英语:duck typing)是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定。这个概念的名字来源于由James Whitcomb Riley提出的鸭子测试(见下面的“历史”章节),“鸭子测试”可以这样表述:“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。” |
| 187 | +读大师的话,醍醐灌顶,豁然开朗,再也不去参与那些浪费唾沫的争论了。 |
134 | 188 |
|
135 | | -对于鸭子类型,也是有争议的。这方面的详细信息,读者可以去看有关维基百科的介绍。 |
| 189 | +顺便再告诉读者,从发表于2003年5月2日的[《Strong Typing vs. Strong Testing》](https://docs.google.com/document/d/1aXs1tpwzPjW9MdsG5dI7clNFyYayFBkcXwRDo-qvbIk/preview)中可以看出,大师在那时已经开始在授课的过程中给学生使用Python了。2003年,那时候赵国程序员,有多少知道这个星球上有一种名为Python的计算机高级语言。 |
136 | 190 |
|
137 | 191 | 对于多态问题,最后还要告诫读者,类型检查是毁掉多态的利器,比如type、isinstance以及isubclass函数,所以,一定要慎用这些类型检查函数。 |
138 | 192 |
|
|
0 commit comments