★面向对象:封装、继承和多态是面向对象的三大特色★
面向对象编程简称OOP,是一种程序设计思想。OOP把对象做为程序的基本单元,一个对象包含了数据和操做数据的函数。
面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行。为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数经过切割成小块函数来下降系统的复杂度
面向对象的程序设计把计算机程序视为一组对象的集合,而每一个对象均可以接收其余对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递
在Python中,全部数据类型均可以视为对象,固然也能够自定义对象。自定义的对象数据类型就是面向对象中的类(Class),给对象发消息实际上就是调用对象对应的关联函数,称之为对象的方法(Method)
Class是一种抽象概念,好比咱们定义的Class——Student,是指学生这个概念,而实例(Instance)则是一个个具体的Student,好比Master.Li
面向对象的设计思想是抽象出Class,根据Class建立Instance。面向对象的抽象程度又比函数要高,由于一个Class既包含数据,又包含操做数据的方法python
一、 类和实例
类是抽象的模板,而实例是根据类建立出来的一个个具体的“对象”,每一个对象都拥有相同的方法,但各自的数据可能不一样编程
定义类是经过class 关键字,class 后面紧接着是类名,类名一般是大写开头的单词,紧接着是(object) ,表示该类是从哪一个类继承下来的类名一般是大写开头的单词,紧接着是(object)
表示该类是从哪一个类继承来的,若是没有合适的继承类,就使用object 类,这是全部类最终都会继承的类函数
class Student(object): pass # 定义好了类,就能够根据类建立出实例,建立实例是经过类名+()实现的 Zhao = Student() print Zhao # <__main__.Student object at 0x0238EB30> print Student # <class '__main__.Student'> # 结果表示:变量Zhao指向的是Student的对象,而Student自己就是一个类 # 能够自由地给实例变量绑定属性,不像其余语言【类,对象,成员变量,属性,方法】 Zhao.name = 'Zhao' Zhao.age = 22 print u'对象Zhao的信息:', Zhao.name, Zhao.age # 因为类能够起到模板的做用,所以,在建立实例的时候能够把一些咱们认为必须绑定的属性强制填写进去【相似于构造函数】 # __init__ 方法的第一个参数永远是self ,表示建立的实例自己,在方法内部,就能够把各类属性绑定到self ,由于self 就指向建立的实例自己 # 有了__init__ 方法,在建立实例的时候,就不能传入空的参数了,必须传入与__init__ 方法匹配的参数,但self不须要传,Python解释器本身会把实例变量传进去 class Student(object): def __init__(self, name, age): self.name = name self.age = age Qian = Student('Qian',22) print u'对象Qian的信息:', Qian.name, Qian.age
运行效果:设计
二、数据封装————面向对象三大特性之一
对于Student实例自己就拥有的数据,不必由外面的函数访问并处理,能够直接封装成类的方法,将数据和逻辑封装起来,用户没必要知道内部实现细节,只要会调用就好了code
class Student(object): def __init__(self, name, age): # 给实例对象绑定属性 self.name = name self.age = age def SetName(self,name): # 设置name self.name = name def SetAge(self,age): # 设置age self.age = age def GetName(self): # 获取name return self.name def GetAge(self): # 获取age return self.age def print_info(self): print 'Name:',self.name,'Age:',self.age Qian = Student('Qian',22) print u'对象Qian的信息:', Qian.name, Qian.age Qian.SetName('Q') Qian.SetAge(23) print u'对象Qian的信息:', Qian.GetName(), Qian.GetAge() Qian.print_info() # ★在类中定义的函数仍然能够用默认参数、可变参数和关键字参数,和普通的函数相比只有一点不一样,就是第一个参数永远是实例变量self ,而且调用时,不用传递该参数。
运行效果:对象
★★小结:类是建立实例的模板,而实例则是一个一个具体的对象,各个实例拥有的数据都互相独立,互不影响
方法就是与实例绑定的函数,和普通函数不一样,方法能够直接访问实例的数据。经过在实例上调用方法,咱们就直接操做了对象内部的数据,但无需知道方法内部的实现细节
和静态语言不一样,Python容许对实例变量绑定任何数据,也就是说,对于两个实例变量,虽然它们属于同一个类的不一样实例,但拥有的变量名称可能不一样继承
三、访问限制
在Class内部,能够有属性和方法,而外部代码能够经过直接调用实例变量的方法来操做数据,这样,就隐藏了内部的复杂逻辑
若是要让内部属性不被外部访问,能够在属性的名称前加上两个下划线__,同理,实例的变量名若是以__开头,表示是一个私有变量(private),只有内部能够访问,外部不能访问.确保了外部代码不能随意修改对象内部的状态,这样经过访问限制的保护,代码更加健壮it
class Student(object): def __init__(self, name, age): # 给实例对象绑定属性 self.__name = name self.__age = age def SetName(self,name): # 设置name self.__name = name def SetAge(self,age): # 设置age self.__age = age def GetName(self): # 获取name return self.__name def GetAge(self): # 获取age return self.__age def print_info(self): print 'Name:',self.__name,'Age:',self.__age Sun = Student('Sun',30) # print u'对象Sun的信息:', Sun.__name, Sun.__age # 'Student' object has no attribute '__name' Sun.print_info() Sun.SetName('S') Sun.SetAge(31) print u'对象Sun的信息:', Sun.GetName(), Sun.GetAge() Sun.print_info() print Sun._Student__name
运行效果:面向对象编程
在Python中,变量名相似__xxx__ 的,也就是以双下划线开头,而且以双下划线结尾的,是特殊变量,特殊变量是能够直接访问的,不是private变量,因此,不能用__name__ 、__score__ 这样的变量名ast
以一个下划线开头的实例变量名,好比_name ,这样的实例变量外部是能够访问的,可是,根据习惯,把这样的变量当作,“虽然能够被访问,可是,视为私有变量,不要随意访问”
双下划线开头的实例变量也不是必定不能从外部访问。不能直接访问__name 是由于Python解释器对外把__name 变量改为了_Student__name ,因此,仍然能够经过_Student__name 来访问__name 变量:
print Sun._Student__name # S
可是强烈建议你不要这么干,由于不一样版本的Python解释器可能会把__name 改为不一样的变量名。
四、继承
定义一个类的时候,能够从某个现有的class继承,新的class称为子类,而被继承的class称为基类、父类或超类
# 继承最大的好处是子类得到了父类的所有功能,并容许增长和修改【不能删除】一些方法,且容许对代码作一点改进 class Animal(object): def run(self): print 'Animal is running...' class Dog(Animal): def run(self):# 函数的重写 print 'Dog is running...' def eat(self):# 增长新功能 print 'Eating food ...' class Cat(Animal): def run(self):# 函数的重写 print 'Cat is running...' def eat(self):# 增长新功能 print 'Eating food ...' d = Dog() d.run() # Dog is running... d.eat() c = Cat() c.run() # Cat is running... c.eat()
运行效果:
五、多态
# 【函数的重写】当子类和父类都存在相同的run() 方法时,子类的run() 会覆盖父类的run() ,在代码运行的时候,老是调用子类的run() # 定义一个class的时候,实际上就定义了一种数据类型。咱们定义的数据类型和Python自带的数据类型,好比str、list、dict没什么两样 # 判断一个变量是不是某个类型能够用isinstance() 判断: l = list()# a是list类型 # l = list[]# a是list变量 print isinstance(l,list) # print isinstance(l,Iterable) a = Animal() print isinstance(a,Animal) d = Dog() print isinstance(d,Dog) print u'子类的实例是否属于父类类型:',isinstance(d,Animal) # 向上转型 print u'父类的实例是否属于某子类类型:',isinstance(a,Cat) # 向下转型 # 【类型上转】在继承关系中,若是一个实例的数据类型是某个子类,那它的数据类型也能够被看作是父类。可是,反过来不行 # 【父类作参数】当使用父类Animal做为函数或方法的参数,能够接收Animal类以及任何Animal子类 def run_twice(animal): animal.run() animal.run() run_twice(Animal()) run_twice(Dog()) run_twice(Cat()) # 因为Animal类型有run()方法,所以,传入的任意类型,只要是Animal类或者子类,就会自动调用实际类型的run()方法 class Monkey(Animal): def run(self): print 'monkey is runing...' run_twice(Monkey())
运行效果:
# 对于一个变量,咱们只须要知道它是Animal类型,无需确切地知道它的子类型,就能够放心地调用run() 方法,而具体调用的run() 方法是做用在Animal、Dog、Cat仍是Monkey对象上,由运行时该对象的确切类型决定
# 这就是多态真正的威力:调用方只管调用,无论细节,而当咱们新增一种Animal的子类时,只要确保run() 方法编写正确,不用管原来的代码是如何调用的。这就是著名的“开闭”原则:
# 对扩展开放:容许新增Animal子类;
# 对修改封闭:不须要修改依赖Animal类型的run_twice() 等函数
# ★★小结:继承能够把父类的全部功能都直接拿过来,这样就没必要重零作起,子类只须要新增本身特有的方法,也能够把父类不适合的方法覆盖重写 # 任何类最终都继承自object类