抽象是隐藏多余细节的艺术。在面向对象的概念中,抽象的直接表现形式通常为类。Python基本上提供了面向对象编程语言的所有元素,如果你已经至少掌握了一门面向对象语言,那么利用Python进行面向对象程序设计将会相当容易。
一、封装
面向对象程序设计中的术语对象(Object)基本上可以看做数据(特性)以及由一系列可以存取、操作这些数据的方法所组成的集合。传统意义上的“程序=数据结构+算法”被封装”掩盖“并简化为“程序=对象+消息”。对象是类的实例,类的抽象则需要经过封装。封装可以让调用者不用关心对象是如何构建的而直接进行使用。
一个简单的Python类封装如下:
Encapsulation _metaclass_=type # 确定使用新式类
class Animal:
def __init__(self): #构造方法 一个对象创建后会立即调用此方法
self.Name="Doraemon "
print self.Name
def accessibleMethod(self): #绑定方法 对外公开
print "I have a self! current name is: "
print self.Name
print "the secret message is: "
self.__inaccessible()
def __inaccessible(self): #私有方法 对外不公开 以双下划线开头
print "U cannot see me... "
@staticmethod
def staticMethod():
#self.accessibleMethod() #在静态方法中无法直接调用实例方法 直接抛出异常
print "this is a static method "
def setName(self,name ): #访问器函数
self.Name=name
def getName(self): #访问器函数
return self.Name
name =property(getName,setName) #属性 可读可写
上面简单的示例代码可以看出,Python中的类包含了一般面向对象编程语言的主要元素,比如构造方法、绑定方法、静态方法、属性等,如果深入下去,还可以发现Python中内置了很多面向对象的“高级”主题,比如迭代器 、反射 、特性 等等,提供了封装的直接构造要素。
二、继承
1、类继承
继承给人的直接感觉是这是一种复用代码的行为。继承可以理解为它是以普通的类为基础建立专门的类对象,子类和它继承的父类是IS-A的关系。一个简单而不失经典的示例如下:
Inheritance _metaclass_=type # 确定使用新式类
class Animal:
def __init__(self):
self.Name="Animal "
def move(self,meters):
print "%s moved %sm. " %(self.Name,meters)
class Cat(Animal): #Cat是Animal的子类
def __init__(self): #重写超类的构造方法
self.Name="Garfield "
## def move(self,meters): #重写超类的绑定方法
## print "Garfield never moves more than 1m. "
class RobotCat(Animal):
def __init__(self): #重写超类的构造方法
self.Name="Doraemon "
## def move(self,meters): #重写超类的绑定方法
## print "Doraemon is flying. "
obj=Animal()
obj.move(10) #输出:Animal moved 10m.
cat=Cat()
cat.move(1) #输出:Garfield moved 1m.
robot=RobotCat()
robot.move(1000) #输出:Doraemon moved 1000m.
一个显而易见的特点是,Python的面向对象的继承特征是基于类 (class)的,比javascript基于原型(prototype)的继承更容易组织和编写代码,也更容易让人接受和理解。
ps:最近几天Anders大神搞出来的TypeScript 横空出世,看它的语言规范里继承也是基于类的:
TypeScript-SimpleInheritance class Animal {
constructor (public name ) { }
move(meters) {
alert(this.name + " moved " + meters + "m. ");
}
}
class Snake extends Animal {
constructor (name ) { super(name ); }
move() {
alert("Slithering... ");
super.move(5);
}
}
class Horse extends Animal {
constructor (name ) { super(name ); }
move() {
alert("Galloping... ");
super.move(45);
}
}
var sam = new Snake("Sammy the Python ")
var tom: Animal = new Horse("Tommy the Palomino ")
sam.move()
tom.move(34)
感觉基于类的继承的语言好像在实现OO的可读性和编程体验上都不太差,据传说javascript是码农最想喷也是喷过最多f**k的语言。
2、多重继承
不同于C#,Python是支持多重类继承的(C#可继承自多个Interface,但最多继承自一个类)。多重继承机制有时很好用,但是它容易让事情变得复杂。一个多重继承的示例如下:
MultInheritance _metaclass_=type # 确定使用新式类
class Animal:
def eat(self,food):
print "eat %s " %food
class Robot:
def fly(self,kilometers):
print "flyed %skm. " %kilometers
class RobotCat(Animal,Robot): #继承自多个超类
def __init__(self):
self.Name="Doraemon "
robot=RobotCat() # 一只可以吃东西的会飞行的叫哆啦A梦的机器猫
print robot.Name
robot.eat("cookies ") #从动物继承而来的eat
robot.fly(10000000) #从机器继承而来的fly
如你所看到的那样,多重继承的好处显而易见,我们可以轻而易举地通过类似“组合”的方式复用代码构造一个类型。
有个需要注意的地方,即如果一个方法从多个超类继承,那么务必要小心继承的超类(或者基类)的顺序:
MultInheritance _metaclass_=type # 确定使用新式类
class Animal:
def eat(self,food):
print "eat %s " %food
def move(self,kilometers): #动物的move方法
pass
class Robot:
def move(self,kilometers): #机器的move方法
print "flyed %skm. " %kilometers
class RobotCat(Animal,Robot): #继承自多个超类 如方法名称相同,注意继承的顺序
#class RobotCat(Robot,Animal):
def __init__(self):
self.Name="Doraemon "
robot=RobotCat() # 一只可以吃东西的会飞行的叫哆啦A梦的机器猫
print robot.Name
robot.eat("cookies ") #从动物继承而来的eat
robot.move(10000000) #本来是要从机器继承move方法,但是因为继承的顺序,这个方法直接继承自动物而pass掉
我们为动物和机器各实现一个相同名称的move方法,但是输出因为继承的顺序而有所不同,Python在查找给定方法或者特性时访问超类的顺序被称为MRO(Method Resolution Order,方法判定顺序)。
三、多态
多态意味着可以对不同的对象使用同样的操作,但它们可能会以多种形态呈现出结果。在Python中,任何不知道对象到底是什么类型,但又需要对象做点什么的时候,都会用到多态。
能够直接说明多态的两段示例代码如下:
1、方法多态
Method-Polymorphism _metaclass_=type # 确定使用新式类
class calculator:
def count (self,args):
return 1
calc=calculator() #自定义类型
from random import choice
obj=choice(['hello,world',[1,2,3],calc]) #obj是随机返回的 类型不确定
#print type(obj)
print obj.count ('a') #方法多态
对于一个临时对象obj,它通过Python的随机函数取出来,不知道具体类型(是字符串、元组还是自定义类型),都可以调用count方法进行计算,至于count由谁(哪种类型)去做怎么去实现我们并不关心。
有一种称为”鸭子类型(duck typing) “的东西,讲的也是多态:
DuckTyping _metaclass_=type # 确定使用新式类
class Duck:
def quack(self):
print "Quaaaaaack! "
def feathers(self):
print "The duck has white and gray feathers. "
class Person:
def quack(self):
print "The person imitates a duck. "
def feathers(self):
print "The person takes a feather from the ground and shows it. "
def in_the_forest(duck):
duck.quack()
duck.feathers()
def game():
donald = Duck()
john = Person()
in_the_forest(donald)
in_the_forest(john)
game()
就in_the_forest函数而言,参数对象是一个鸭子类型,它实现了方法多态。但是实际上我们知道,从严格的抽象来讲,Person类型和Duck完全风马牛不相及。
2、运算符也多态
Operator-Polymorphism def add (x,y):
return x+y
print add (1,2) #输出3
print add ("hello, ","world ") #输出hello,world
print add (1,"abc ") #抛出异常 TypeError: unsupported operand type(s) for +: 'int' and 'str'
上例中,显而易见,Python的加法运算符是”多态“的,理论上,我们实现的add方法支持任意支持加法的对象,但是我们不用关心两个参数x和y具体是什么类型。
一两个示例代码当然不能从根本上说明多态。普遍认为面向对象最有价值最被低估的特征其实是多态。我们所理解的多态的实现和子类的虚函数地址绑定有关系,多态的效果其实和函数地址运行时动态绑定有关。在C#中实现多态的方式通常有重写 和重载 两种,从上面两段代码,我们其实可以分析得出Python中实现多态也可以变相理解为重写和重载。在Python中很多内置函数和运算符都是多态的。
号外 :在C#中,我们熟知接口(Interface)和多态相关,在处理多态对象时,只要关心它的接口(或者称为“协议”)而不需要显式指定特定实现类型即可,这也是IoC中面向接口(抽象)而不依赖于具体实现编程的基础。可惜在Python中根本没有Interface(有抽象类)。而在TypeScript的Specification 中引入了Interface的概念:
TypeScript-Interface interface Friend {
name : string;
favoriteColor?: string;
}
function add(friend: Friend) {
var name = friend.name ;
}
add({ name : "Fred " }); // Ok
add({ favoriteColor: "blue " }); // Error, name required
add({ name : "Jill ", favoriteColor: "green " }); // Ok
TypeScript语言规范里对Interface的定义是命名的对象类型(Programmers can give names to object types; we call named object types interfaces . )。它直接等价于下面的Ojbect Type:
var Friend: () => {
name : string; favoriteColor?: string;
};
上面这个Object Type在Playgound 中等价的javascript代码如下:
var Friend;
在TypeScript编程规范文档里,对Interface的一些说明:
Interfaces provide the ability to give names to object types and the ability to compose existing named object types into new ones. Interfaces have no run-time representation—they are purely a compile-time construct. Interfaces are particularly useful for documenting and validating the required shape of properties, objects passed as parameters, and objects returned from functions .
Interface可以继承自Interface:
TypeScript-InterfaceInheritInterface interface Mover
{
move(): void ;
getStatus(): { speed: number; };
}
interface Shaker
{
shake(): void ;
getStatus(): { frequency: number; };
}
interface SimpleMover extends Mover
{
}
interface MoverShaker extends Mover, Shaker
{
getStatus(): { speed: number; frequency: number; };
}
我们尝试让类继承自Interface:
TypeScript-ClassInheritInterface interface Mover
{
move(): void ;
}
class SimpleMover extends Mover{
move() {
alert ("move ")
}
}
var mover=new SimpleMover();
mover.move();
实践证明类是不能继承实现Interface的:A export class may only extend other classes , Mover is an interface.
现在大家知道TypeScript里的Interface和我们所认识和理解的接口的区别了吗?相同的一个名词Interface,不同语境下意义并不完全相同,看上去好像是挺傻的,但是学习和使用语言不做对比几乎是不可能的。
参考:
http://www.python.org/
http://zh.wikipedia.org/wiki/Duck_typing
http://typescript.codeplex.com/
http://www.typescriptlang.org/Playground/
http://www.nczonline.net/blog/2012/10/04/thoughts-on-typescript/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构