鸭子类型是什么

经常听别人说Python是一个动态语言,而鸭子类型是Python重要的设计哲学之一。鸭子类型(duck typing)的原话是:

如果看起来像鸭子、叫起来像鸭子、走起路来也像鸭子,那它就是鸭子

用Python的角度来看,就是如果某个类实现了Duckwalk quack等鸭子有的方法,就可以把它视为是鸭子,即它和Duck是同一种类型的

如果没接触过静态语言(C/C++/Java)的人可能就会疑惑,这样有什么用?

于是先以C++为例介绍一下。

C++向函数传参时必须指明参数的类型。这时候问题来了,假设有很多种近似的东西,假设都是禽类,都有walk()方法(C++中好像叫成员函数):

1
2
3
class Duck { /* ... */ };
class Chicken { /* ... */ };
class Goose { /* ... */ };

这时候我要编写一个函数,能够同时接收这三种类型的其中一种,并调用其的walk()函数。

由于必须指明其参数类型,又因为类型限制,所以C++的做法是定义一个基类Bird,将上面的三个类都继承于基类,并将walk()定义为虚函数,这三个类再分别实现walk()函数,这时候只需要在函数中指明传递基类Bird的引用/指针,然后在函数类调用引用的walk()函数就可以了。这就是面向对象中的多态,由于对象的真正的类型是在运行时才确定的,在C++中称为动态绑定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Bird {
public:
    virtual void walk()=0;
};

class Duck : public Bird {
public:
    void walk() { cout << "Duck is walking..." << endl; }
};
class Chicken : public Bird {
public:
    void walk() { cout << "Chicken is walking..." << endl; }
};
class Goose: public Bird {
public:
    void walk() { cout << "Goose is walking..." << endl; }
};

void testBird(Bird &b)
{
    b.walk();
}

再回到Python上来,由于Python的鸭子类型特性,就不用重新定义一个抽象的基类,直接传参即可。但是 还是建议定义一个抽象类,因为这样更加容易理解

这样典型的例子有很多,最著名的就是Python中的file和StringIO,它们都实现了相同的方法,只是用途不同而已

优点

能使编程更加灵活,便于测试

缺点

需要对程序十分理解,要有良好的注释和说明,由于运行时类型是动态变化的,容易报AttributeError和TypeError

个人感觉Duck typing虽然灵活,但编程难度也上升了