Python面向对象编程
一、什么是面向对象
面向对象编程(OOP,Object Oriented Programming)
OOP三个主要的特征:数据封装、继承和多态。
所有的程序是由一定的属性和行为对象组成的,不同的对象的访问通过函数调用来完成,对象间所有的交流都是通过方法调用,通过对封装对象数据,提高复用率。
二、创建类和对象
面向对象编程是一种编程方式,此编程方式的落地需要使用 "类" 和 "对象"来实现,所以,面向对象编程其实就是对 "类" 和 "对象" 的使用。
类就是一个模板,模板里可包含多个函数,函数里实现一些功能。对象则是根据模板创建的实例,通过实例对象可以执行类中的方法。
class Role (object): def __init__(self,name): self.name = name def buy_weapon(self,weapon): pass
class是关键字,定义一个类,其名称为Role,这个类继承了Python的父类,名为object。在类的下面可以看到有很多函数的定义,但在 class中,函数被称为方法,所以这里暂时在一个名为Role类的下面有__init__和buy_weapon方法。
三、封装
封装,顾名思义就是将内容封装到某个地方,以后再去调用被封装在某处的内容。
所以,在使用面向对象的封装特性时,需要:1.将内容封装到某处 2.从某处调用被封装的内容
class Role (object): #object是父类,继承父类 def __init__(self,name,role,weapon,life_value): #称为构造方法,根据类创建对象时自动执行 self.name = name #成员变量,self 代表传进来的name,在整个类都可以使用这个变量,把一个局部变量变为了全局变量 self.role = role self.weapon = weapon self.life_val = life_value def buy_weapon(self,weapon): print("[%s] is buying [%s]" % (self.name,weapon)) self.weapon = weapon # 把一个抽象的类变成一个具体的过程叫实例化 t1 = Role("Stanley",'Terrorist','Glock',100) #等价于Role(t1,"Stanley",'Terrorist','b11',100),同时将'Stanley'、'Terrorist'、'Glock'和100封装到t1和self的name、role、weapon、life_value属性中 p1 = Role("Tom",'Police','Desert Eagle',90) #Role(p1,"Tom",'Police','Desert Eagle',90) print("t1's weapon was [%s] before" % t1.weapon) print("=".center(40,'=')) t1.buy_weapon('AK47') #转换为Role.buy_weapon(t1,'AK47') print("t1's weapon is [%s] now" % t1.weapon) Result: t1's weapon was [Glock] before ======================================== [Stanley] is buying [AK47] t1's weapon is [AK47] now
第一个方法__init__是初始化构造方法,构造方法的第一个参数永远是self,表示这个类的对象本身,真正构造对象时,self这个参数不用写,python编译器会自己加上去,构造方法的作用就是对self对象进行赋值,如下面传进来的name赋给self.name,这么做的目的是因为在class中,每个方法依旧是要遵循函数规则的,在函数下面每个变量都是局部变量,在其他函数中并不能相互调用,如此做就把一个局部变量变为全局变量。
通过self间接调用被封装的内容:
第二个方法除了编译器会自己加上去的self参数外,weapon参数接收传入的变量,打印调用的参数。在print之后self.name含义是将调用者的name赋值给self。
接下来要对p1和t1进行实例化,这是将一个抽象的类变成一个具体的过程,生成了p1和t1两个角色。t1 = Role("Stanley",'Terrorist','Glock',100),Python解释器会将此转换为 Role(t1,"Stanley",'Terrorist','Glock',100),这里通过解释器转换后的t1其实就是self。
现在t1角色进行购买行为,t1.buy_weapon('AK47'),Python解释器会将此转换为 Role.buy_weapon(t1,'AK47'),如上所述此时的t1就是self,也可理解为将t1传入__init__初始化构造方法,就可直接self.name(等价于t1.name)获取到name的变量了(Stanley)。
综上所述,对于面向对象的封装来说,其实就是使用构造方法将内容封装到对象中,然后通过对象直接或者self间接获取被封装的内容。
四、继承
继承,面向对象中的继承和现实生活中的继承相同,即:子可以继承父的内容,也可理解为由一般到特殊。
class SchoolMember(object): member_nums = 0 def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex self.enroll() def enroll(self): print("NO.%s SchoolMember [%s] is enrolled!" % (SchoolMember.member_nums,self.name)) SchoolMember.member_nums += 1 #统计人员数量不可写为self.member_nums += 1,因self实例下没有此变量,两者毫无关联。 def tell(self): print("Hello, My name is [%s]" % self.name) class Teacher(SchoolMember): #参数为继承对象 def __init__(self,name,age,sex,course,salary): #如果不写__init__默认就继承了父类的所有属性,但子类需要额外扩展,需先重写再继承 super(Teacher,self).__init__(name,age,sex) #上面进行了重写,再继承回来进行覆盖,推荐新式类语法 #SchoolMember.__init__(self,name,age,sex) #此为经典类语法,不推荐 self.course = course self.salary = salary def teaching(self): print("Teacher [%s] is teaching [%s]" % (self.name,self.course)) class Student(SchoolMember): def __init__(self,name,age,sex,course,tuition,): super(Student,self).__init__(name,age,sex) self.course = course self.tuition = tuition def pay_tuition(self): print("Student [%s] pays tuition [%s] again" % (self.name,self.tuition)) #实例化老师 t1 = Teacher('Stanley',24,'M','Python',1000) #实例化,不可实例化SchoolMember,其只可用来继承 t1 = Teacher('Katrina',29,'M','MySQL',1500) #实例化,不可实例化SchoolMember,其只可用来继承 #实例化学生 s1 = Student('Tim',24,'F','MySQL',10000) s2 = Student('Bella',27,'M','Python',12000) print("===========子类使用父类方法============") t1.tell() s1.tell() print("===========子类使用私有方法============") t1.teaching() s1.pay_tuition() Result: NO.0 SchoolMember [Stanley] is enrolled! NO.1 SchoolMember [Katrina] is enrolled! NO.2 SchoolMember [Tim] is enrolled! NO.3 SchoolMember [Bella] is enrolled! ===========子类使用父类方法============ Hello, My name is [Katrina] Hello, My name is [Tim] ===========子类使用私有方法============ Teacher [Katrina] is teaching [MySQL] Student [Tim] pays tuition [10000] again
此为标准继承示例,其中包含三个类,SchoolMember是父类,Teacher和Student是子类,子类中拥有父类的共同属性,除此之外,子类还需要额外进行扩展,所以在子类中要先重写,再从父类继承回来进行覆盖。在继承语法中super为关键字,不可省略,其参数为子类名称,self,__init__参数为需要继承的属性。相关封装特性详见上一段落,不再累述。父类和子类定义好后,实例化Teacher和 Student,在父类中执行注册方法,每个子类都可调用父类的tell方法,并且每个子类都会包含私有方法teaching和pay_tuition,这就利用继承特性实现了简单的权限限制功能。
综上所述,对于面向对象的继承来说,其实就是将多个类共有的方法提取到父类中,子类仅需继承父类而不必一一实现每个方法。
五、多态
多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。
Pyhon不支持多态并且也用不到多态,多态的概念是应用于Java和C#这一类强类型语言中的。那么,多态的作用是什么呢?我们知道,封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了——代码重用。而多态则是为了实现另一个目的——接口重用!多态的作用,就是为了类在继承和派生的时候,保证使用“家谱”中任一类的实例的某一属性时的正确调用。
通过Python模拟的多态:
class Animal: def __init__(self, name): # Constructor of the class self.name = name def talk(self): # Abstract method, defined by convention only raise NotImplementedError("Subclass must implement abstract method") class Cat(Animal): def talk(self): return 'Meow!' class Dog(Animal): def talk(self): return 'Woof! Woof!' animals = [Cat('Missy'), Dog('Lassie')] for animal in animals: print animal.name + ': ' + animal.talk()