1.封装
- 封装:将数据与具体操作的实现代码放在某个对象内部,使这些代码的实现细节不被外界发现,外界只能通过接口使用该对象,而不能通过任何形式修改对象内部实现,正是由于封装机制,程序在使用某一对象时不需要关心该对象的数据结构细节及实现操作的方法。使用封装能隐藏对象实现细节,使代码更易维护,同时因为不能直接调用、修改对象内部的私有信息,在一定程度上保证了系统安全性。类通过将函数和变量封装在内部,实现了比函数更高一级的封装。
class Student:
def __init__(self,name, age):
self.name = name
self.age = age
def printInfo(self):
print("name:{}".format(self.name), "age:{}".format(self.age))
# 下面的操作都是错误的!原因:类将它内部的变量和方法封装起来,阻止外部的直接访问
print(name)
print(age)
printInfo()
2.继承
- 继承:定义一个新类的时候,新的类称为子类(Subclass),而被继承的类称为基类、父类或超类(Base class、Super class)。继承最大的好处是子类获得了父类的全部变量和方法的同时,又可以根据需要进行修改、扩展。其语法结构如下:
class Foo(superA, superB,.... ):
class DerivedName(modelname.BaseName):
- Python支持多父类的继承机制,所以需要注意圆括号中基类的顺序,若是基类中有相同的方法名,并且在子类使用时未指定,Python会从左至右搜索基类中是否包含该方法,一旦查找到则直接调用,后面不再继续查找。
# 父类定义
class people:
def __init__(self, name, age, weight):
self.name = name
self.age = age
self.__weight = weight
def speak(self):
print("%s 说: 我 %d 岁。" % (self.name, self.age))
# 单继承示例
class student(people):
def __init__(self, name, age, weight, grade):
# 调用父类的实例化方法
people.__init__(self, name, age, weight)
self.grade = grade
# 重写父类的speak方法
def speak(self):
print("%s 说: 我 %d 岁了,我在读 %d 年级" % (self.name, self.age, self.grade))
s = student('ken', 10, 30, 3)
s.speak()
- Python3中的继承原则,必须记忆:
-
原则1:子类在调用某个方法或变量的时候,首先在自己内部查找,如果没有找到,则开始根据继承机制在父类里查找。
class D: pass class C(D): pass class B(C): def show(self): print("i am B") pass class G: pass class F(G): pass class E(F): def show(self): print("i am E") pass class A(B, E): pass a = A() a.show()
- 解释:运行结果是"i am B"。在类A中,没有show()这个方法,于是它只能去它的父类里查找,它首先在B类中找,结果找到了,于是直接执行B类的show()方法。可见,在A的定义中,继承参数的书写有先后顺序,写在前面的被优先继承。
-
原则2:根据父类定义中的顺序,以深度优先搜索(BFS)的方式逐一查找父类!
- 情况1:如果B没有show方法,而是D有呢?
class D: def show(self): print("i am D") pass class C(D): pass class B(C): pass class G: pass class F(G): pass class E(F): def show(self): print("i am E") pass class A(B, E): pass a = A() a.show()
- 解释:执行结果是"i am D",左边具有深度优先权,当一条路走到黑也没找到的时候,才换另一条路。可见,在这种继承结构关系中,搜索顺序是这样的:
- 情况2:如果继承结构是这样的呢?,如下图所示。类D和类G又同时继承了类H。当只有B和E有show方法的时候,无疑和上面的例子一样,找到B就不找了,直接打印"i am B"。但如果是只有H和E有show方法呢?
class H: def show(self): print("i am H") pass class D(H): pass class C(D): pass class B(C): pass class G(H): pass class F(G): pass class E(F): def show(self): print("i am E") pass class A(B, E): pass a = A() a.show()
- 解释:在这种情况下,打印的是"i am E",Python的搜索路径是这样的:
- 情况3:对于其他继承情况,可以拆分成上面两个情况,如下图所示:
class D(): pass class G(): def show(self): print("i am G") pass class F(G): pass class C(D): pass class B(C,F): pass class E(F): def show(self): print("i am E") pass class A(B, E): pass
-
- super()函数:在子类中如果有与父类同名的成员,那就会覆盖掉父类里的成员。那如果想强制调用父类的成员呢?使用super()函数!这是一个非常重要的函数,最常见的就是通过super调用父类的实例化方法__init__!使用方法super(子类名,self).方法名(),需要传入的是子类名和self,调用的是父类里的方法,按父类的方法需要传入参数。
class A: def __init__(self, name): self.name = name print("父类的__init__方法被执行了!") def show(self): print("父类的show方法被执行了!") class B(A): def __init__(self, name, age): super(B, self).__init__(name=name) self.age = age def show(self): super(B, self).show() obj = B("jack", 18) obj.show()
3.多态
class Animal:
def kind(self):
print("i am animal")
class Dog(Animal):
def kind(self):
print("i am a dog")
class Cat(Animal):
def kind(self):
print("i am a cat")
class Pig(Animal):
def kind(self):
print("i am a pig")
# 这个函数接收一个animal参数,并调用它的kind方法
def show_kind(animal):
animal.kind()
d = Dog()
c = Cat()
p = Pig()
show_kind(d)
show_kind(c)
show_kind(p)
- 狗、猫、猪都继承了动物类,并各自重写了kind方法。show_kind()函数接收一个animal参数,并调用它的kind方法。可以看出,无论我们给animal传递的是狗、猫还是猪,都能正确的调用相应的方法,打印对应的信息,这就是多态。
- 由于Python的动态语言特性,传递给函数show_kind()的参数animal可以是任何的类型,只要它有一个kind()的方法即可。动态语言调用实例方法时不检查类型,只要方法存在,参数正确,就可以调用。这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。
class Job:
def kind(self):
print("i am not animal, i am a job")
j = Job()
show_kind(j)