1. 封装
封装是OPP的特征之一, 是对象和类概念的主要特征。 封装,即把客户实物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐瞒; 其实,就是对数据属性提供借口和访问函数。 在python中,所有的类属性都是公开的,但是名字可能被“混淆”了,以阻止未经授权的访问,仅此而已,没有其他预防措施。
封装的作用:
- 防止对象的数据被随意修改
- 使外部程序不需要关注对象内部的构造,只需要通过此对象对外提供的接口进行直接访问即可
2. 继承
2.1 定义
继承,即子类属性从祖先类继承的一种方式。 子类的创建,称为派生;子类也叫“派生类”。 被继承的称为“基类”,“父类”或者“超类”。 继承的作用,子类保留了已存类(即父类)类型中所有的数据和行为,但允许修改或则其他的自定义操作。
要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。
class Person(object): def __init__(self, name, age): # 父类构造函数,子类可继承 self.name = name self.age = age self.sex = "normal" def talk(self): print("%s is talking..." % self) class Black_person(Person): # 继承:black_person def __init__(self,name,age,strenghth): # 先继承,再重构 Person.__init__(self,name,age) # 子类的name 作为参数传入父类 self.strenghth = strenghth print(self.name, self.age, self.sex) def talk(self): print("black person %s is talking..." %self.name) def walk(self): print("is walking...") class white_person(Person): pass b = Black_person("Will Smith", 45, "strong") b.talk() b.walk()
子类的“先继承,再重构”的逻辑:
2.2 一个子类可以继承多个基类
在某些 OOP 语言中,一个子类可以继承多个基类 (见 school 例子中的 teacher)。 但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。多级继承,一层层继承下去;一般也就三层继承。
继承概念的实现方式主要有2类:实现继承、接口继承。
- 实现继承是指使用基类的属性和方法而无需额外编码的能力;子类直接继承父类的方法。
- 接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力(子类重构爹类方法);子类重新父类的方法。
在考虑使用继承时,有一点需要注意,那就是两个类之间的关系应该是“属于”关系。例如,Employee 是一个人,Manager 也是一个人,因此这两个类都可以继承 Person 类。但是 Leg 类却不能继承 Person 类,因为腿并不是一个人。
抽象类仅定义将由子类创建的一般属性和方法。
class SchoolMember(object): ''' 学校成员基类 ''' member = 0 # 计数器, 公有属性 def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex self.enroll() # enroll的方法,无论老师还是学生都有,所以就写入基类。自动调用注册 def enroll(self): ''' 注册 ''' print('new school member [%s] enrolled' % self.name) SchoolMember.member +=1 # 注意此次是schoolmember def tell(self): # 打印不同对象的属性 print('-------info: %s-------' % self.name) for k,v in self.__dict__.items(): print(" ", k, v) print('----------end----------') ''' def __del__(self): print("开除了[%s]" % self.name) SchoolMember.member -=1 ''' class School(object): ''' 学校类 ''' def open_branch(self, addr): print('openning a new branch in ', addr) class Teacher(SchoolMember, School): # 子类Teacher 同时继承 父类 SchoolMember和School. python是支持多继承的语言, 不过不常用。 ''' 讲师 ''' def __init__(self, name, age, sex, salary, course): SchoolMember.__init__(self, name, age, sex) self.salary = salary self.course = course #self.enroll() # 写入基类 def teaching(self): print('Teacher [%s] is teaching [%s]' %(self.name, self.course)) class Student(SchoolMember): def __init__(self, name, age, sex, course, tuition): SchoolMember.__init__(self, name, age, sex) self.course = course self.tuition = tuition #self.enroll() # 写入基类 def pay_tuition(self, amount): print("student [%s] has just paid [%s]" %(self.name, amount)) self.amount += amount t1 = Teacher('wu sir', 28, 'FM', 3000, 'PYTHON') s1 = Student('jak', 38, 'na', 'pys15', 300000) s2 = Student('lu', 12, 'm', 'pys15', 11000) print(t1.__dict__) # 返回:{'name': 'wu sir', 'age': 28, 'sex': 'FM', 'salary': 3000, 'course': 'PYTHON'} print(SchoolMember.member)
2.3 经典类 vs. 新式类
新式类时下比较流行;同时,经典类和新式类主要有两大区别:语法不通及在python 2.x中继承顺序的不通(在python 3.x中都是一样的)
2.3.1 新式类: 定义时必须继承object类。 初始化构造函数用super关键字来继承
# 基类的定义 class Person(object): pass # 继承构造方法 super(子类,self).__init_(name, age)
新式类的继承顺序如下:
class A(object): def __init__(self): self.n = "A" class B(A): def __init__(self): self.n = "B" class C(A): def __init__(self): self.n = "C" class D(B,C): def __init__(self): self.n = "D"
2.3.2 经典类:
# 基类的定义 class Person: # 继承构造方法: 父类.__init__(self,name,age)。
在python 2.x 中, 经典类的继承顺序如下(bug), 运用 深度优先查询 :
OO开发范式大致为:划分对象→抽象类(person)→将类组织成为层次化结构(继承和合成) →用类与实例进行设计和实现几个阶段。类名都需要首字母大写
3. 多态
在自动化的代码里,一般多态和继承用的不多。 多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现,这就是同一种事物表现出的多种形态。
class Animal(object): def __init__(self, name): self.name = name def talk(self): raise NotImplementedError("Subclass must implement abstract method") # 在此主动出现错误,主要是提供子类的一个借口 class Cat(Animal): def talk(self): print('%s: miao miao miao' %self.name) class Dog(Animal): def talk(self): print('%s: woof woof woof' %self.name) def func(obj): # 一个借口,多种形态; 在python中,父级不能调用子级,所以需要定义一个函数满足。 obj.talk() c1 = Cat('cat 1.0') d2 = Dog('dog 1.0') func(c1) func(d2)
注意,之后讲到 @staticmethod, 就可以在父类中设置一个talk2 - 内部调用talk,也能做到多态的效果。 但这里,用函数调用的方式。