面向对象的程序设计提供了一种新的思维方式,软件设计的焦点不再是程序的逻辑流程,而是软件或程序中的对象以及对象之间的关系。使用面向对象的思想进行程序设计,额能够更好地设计软件架构,维护软件模块,并易于框架和组件的重用。
Python支持面向过程、面向对象、函数式编程等多种编程范式。Python不强制我们使用任何一种编程范式,我们可以使用过程是编程编写任何程序,在编写小程序(少于500行代码)时,基本上不会有任何问题。但对于中等和大型项目来说,面向对象讲给我们带来很多优势。本文主要介绍面向对象的基本概念和Python语法的特性讲解面向对象的编程。
类和对象基本概念
类和对象是面向对象中的两个重要概念。类是对客观世界中的事务的抽象,而对象是类实例化的实体。类是一种抽象的数据类型。它们的关系是,对象是类的实例,类是对象的模板。
类的定义
Python中使用class关键字定义一个类,类名的首字母一般要大写。当程序员需要创建的类型不能使用简单类型来表示时,则需要定义类,然后利用定义的类创建对象。类把需要使用的变量和方法组合在一起,这种方式称为封装。类的定义方式如下:
#继承自object class Class_name(object): ... #不显式继承自object class Class_name: ...
这两种写法没有区别,可以根据个人喜欢的风格使用。类必须用关键字class定义,关键字class后面是类名。类的主体由一系列的属性和方法组成。通过定义一个水果类来看下类的定义的具体使用方法:
#类的创建 class Fruit(object): def __init__(self, name, color): #__init__为类的构造函数 self.name = name self.color = color def grow(self): #定义grow函数,类中的函数称为方法。类的函数至少有1个self参数 print "Fruit grow..."
对象的创建
创建对象的过程称为实例化。当一个对象被创建后,包含3个方面的特性:对象的句柄、属性和方法。对象的句柄用于区分不同的对象,当对象被创建后,该对象会获取一块存储空间,存储空间的地址即为对象的标识。对象的属性和方法与类的成员变量和成员函数相对应。Python实例化是通过类名加圆括号的方式。实例化上述定义的水果类,格式如下:
#-*- coding:utf-8 -*- class Fruit(object): def __init__(self, name, color): self.name = name # self.color = color def grow(self): print "Fruit grow..." if __name__ == '__main__': apple = Fruit('apple','red') #实例化水果类 print apple.name # apple print apple.color #red
类的属性
类由属性和方法组成。类的属性是对数据的封装,而类的方法则表示对象具有的行为。类通常由函数和变量组成。Python的构造函数、析构函数、私有属性和方法都是通过名称约定区分的。
(1)私有属性和公有属性
类的属性一般分为私有属性和公有属性,不同于Java、C++等语言通过使用保护类型的修饰符去区分私有属性和公有属性,由于Python没有保护类型的修饰符,Python类的私有属性和共有属性是通过约定属性名称来达到数据封装的目的。如果属性的名字以两个下划线开始,就表示为私有属性。反之,则表示公有属性。
#-*- coding:utf-8 -*- class Fruit(object): def __init__(self, name, color): self.__name = name #私有变量使用双下划綫开始 self.color = color def grow(self): print "Fruit grow..." if __name__ == '__main__': apple = Fruit('apple','red') #实例化水果类 #print apple.__name #报错AttributeError: 'Fruit' object has no attribute '__name' print apple._Fruit__name #apple print apple.color #red
类的私有变量不能使用实例直接访问,Python解释器会报错提示属性不存在。Python提供了直接访问私有属性的方式,可用于程序的测试和调试。私有属性访问的格式如下所示:
instance._classname__attribute
也就是实例名._类名__属性名的方式。如例子中的apple._Fruit__name。但是这种直接暴露数据的做法是不提倡的。因为这种方式可以随意更改实例属性的值,会导致程序数据安全方面的问题。这种访问方式主要用于开发阶段的测试或调试时使用。通过的做法是定义相关的get方法获取实例属性的值。例如在Fruit类中定义getColor()方法,后续会介绍方法的定义和使用。
(2)实例属性和静态属性
Python的属性分为实例属性和静态属性。实例属性是以self作为前缀的属性。__init__方法即Python类的构造函数。如果__init__方法中定义的变量没有使用self作为前缀声明,则该变量只是普通的局部变量。类中其他方法定义的变量也只是局部变量,而非类的实例属性。
#-*- coding:utf-8 -*- class Fruit(object): def __init__(self, name, color, zone): self.__name = name #name、color变量加前缀self表示实例属性,可以被实例化对象调用 self.color = color zone = zone #zone变量不加前缀self,只是普通的变量,不能被实例化对象调用 def grow(self): print "Fruit grow..." if __name__ == '__main__': apple = Fruit('apple','red', 'Shannxi') #实例化水果类 print apple._Fruit__name #apple print apple.color # red #print apple.zone # 报错AttributeError: 'Fruit' object has no attribute 'zone'
例子中定义的局部变量zone不能被Fruit的实例化对象调用。
(2)静态变量
Java、C#中有一类特殊的属性称为静态变量。静态变量可以被类直接调用,而不被实例化对象调用。当创建新的实例化对象后,静态变量并不会获得新的内存空间,而是使用类创建的内存空间。因此,静态变量能够被多个实例化对象共享。在Python中静态变量称为类变量,类变量可以在该类的所有实例中被共享。
#-*- coding:utf-8 -*- class Fruit(object): price = 0 #类属性 def __init__(self, name, color, zone): self.__name = name #name、color变量加前缀self表示实例属性,可以被实例化对象调用 self.color = color zone = zone #zone变量不加前缀self,只是普通的变量,不能被实例化对象调用 def grow(self): print "Fruit grow..." if __name__ == '__main__': apple = Fruit('apple','red', 'Shannxi') #实例化水果类 print Fruit.price # 0 Fruit.price = Fruit.price + 100 print "apple's prices = %d" % (apple.price) #100 banana = Fruit('banana', 'yellow', 'Hainan') print "banana's prices = %d" % (banana.price) #100 apple.price = 150 #修改apple对象中price的值,与banana对象无关。 print "apple's prices = %d" % (apple.price) #150 print "banana's prices = %d" % (banana.price) #100 banana.price = 200 #修改banana对象中price的值,与apple对象无关 print "apple's prices = %d" % (apple.price) #150 print "banana's prices = %d" % (banana.price) #200 Fruit.price = Fruit.price + 100 #使用类调用修改price值,不影响apple、banana对象中price的值 print "apple's prices = %d" % (apple.price) #150 print "banana's prices = %d" % (banana.price) #200 print Fruit.price #200
Python对象类的属性和方法定义次序并没有要求。合理的方式是把类属性定义在类中最前面,然后再定义私有方法,最后定义公有方法。
类的方法
(1)普通方法和静态方法
类的方法也分为公有方法和私有方法。私有方法不能被模块外的类或方法调用,私有方法也不能被外部的类或函数调用。C#、Java中的静态方法使用关键字static声明,而Python使用函数staticmethod()或@staticmethod修改器把普通的函数转换为静态方法。Python的静态方法并没有和类的实例进行绑定,要调用只需使用类名作为它的前缀即可。下面这段代码演示了类的方法和静态方法的使用。
#-*- coding:utf-8 -*- class Fruit(object): price = 100 #类属性 def __init__(self, name, color, zone): self.__name = name self.color = color def getColor(self): print self.color def getName(self): print self.__name def setColor(self, color): self.color = color def setName(self, name): self.__name = name @staticmethod #方法一,定义类的静态方法,调用类变量,getPrice中不带self参数 def getPrice(): print Fruit.price def getPrice2(): print Fruit.price tran_getPrice2 = staticmethod(getPrice2) ##方法二,定义类的静态方法,调用类变量 if __name__ == '__main__': apple = Fruit('apple','red', 'Shannxi') #实例化水果类 apple.getColor() # red apple.getName() #apple apple.setColor('green') apple.getColor() #green apple.setName('new_apple') apple.getName() #new_apple Fruit.getPrice() Fruit.tran_getPrice2()
上述代码中getColor()方法中有1个self参数,该参数是区别方法和函数的标志。类的方法至少需要1个参数,调用方法时不必给改参数赋值。通过长这个特殊的参数被命名为self,self参数表示指向实例对象本身。self参数用于区分函数和类的方法。self参数等价于Java、C#中的this关键字,但self必须显示使用,因为Python是动态语言,没有提供声明变量的方式,这样就无法知道在方法中要赋值的变量是不是局部变量或是否需要保存成实例属性。当调用Fruit类的getColor()方法时,Python会把函数的调用转换为grow(fruit)。Python自动完成了对象fruit的传递任务。
(2)类方法
Python中还有一种方法称为类方法。类方法是将类本身作为操作对象的方法。类方法可以使用函数classmethod()或@classmethod修饰器定义。而与实力方法不同的是,把类作为第一个参数(cls)传递。把上述程序的静态方法修改为类方法,修改后的代码如下:
@classmethod #方法一,定义类方法,调用类变量,getPrice中不带self参数 def getPrice(cls): print cls.price def getPrice2(cls): print cls.price tran_getPrice2 = classmethod(getPrice2) ##方法二,定义类方法,调用类变量
可见,类方法的使用和静态方法十分相似。如果某个方法需要被其他实例共享,同时又需要使用当前实例的属性,则定义为类方法。