Python学习之路(36)——面向对象
封装
封装:将内容封装到某个地方,后续再去调用被封装在某处的内容。
1、将内容封装到某处
class Foo:
def __init__(self, name, age):
self.name = name
self.age = age
obj1 = Foo('nicolas', 18)
obj2 = Foo('xiaomi', 2018)
self是一个形参,当执行obj1 = Foo('nicolas', 18)时,self等于obj1。(obj2一样)
'nicolas'和18这2个内容被封装到了对象 obj1 和 obj2 中,每个对象都有name属性和age属性。
2、从某处调用被封装的内容
调用被封装内容有2种方式:
1)通过对象直接调用
class Foo:
def __init__(self, name, age):
self.name = name
self.age = age
obj1 = Foo('nicolas', 18)
print(obj1.name)
print(obj1.age)
######执行结果######
nicolas
18
2)通过self间接调用(执行类中的方法时,需要通过self间接调用被封装的内容)
class Foo:
def __init__(self, name, age):
self.name = name
self.age = age
def detail(self):
print(self.name)
print(self.age)
obj1 = Foo('nicolas', 18)
obj1.detail()
######执行结果######
nicolas
18
综上所述,对于面向对象的封装来说,其实就是使用构造方法将内容封装到对象中,然后通过对象直接或self间接获取被封装的内容。
继承
继承:面向对象中的继承和现实生活中的继承相同,即子可以继承父的内容。
class Animal:
def eat(self):
print("%s eat" % self.name)
def drink(self):
print("%s drink" % self.name)
class Cat(Animal):
def __init__(self, name):
self.name = name
self.breed = '猫'
def cry(self):
print('喵喵叫')
class Dog(Animal):
def __init__(self, name):
self.name = name
self.breed = '狗'
def cry(self):
print('汪汪叫')
c1 = Cat('小白猫')
c2 = Cat('小黑猫')
d1 = Dog('大黄狗')
c1.eat()
c2.drink()
d1.eat()
c1.cry()
d1.cry()
######执行结果######
小白猫 eat
小黑猫 drink
大黄狗 eat
喵喵叫
汪汪叫
对于面向对象的继承来说,其实就是讲多个类共有的方法提取到父类(基类)中,子类(派生类)仅需继承父类而不必一一实现每个方法。
多重继承
Python的类可以继承多个类,如果继承的多个类中每个类都定义了相同的函数,那么子类会继承哪一个类的函数呢?寻找的方法有两种:深度优先和广度优先
1)当类为经典类时,多重继承情况下,会按照深度优先方式查找
2)当类为新式类时,多重继承情况下,会按照广度优先方式查找

(注意:Python 2.x中默认都是经典类,只有显示继承了object才是新式类;Python 3.x中默认都是新式类,不必显式的继承object)
Python 2.x示例(经典类):
class D:
def bar(self):
print('D.bar')
class C(D):
def bar(self):
print('C.bar')
class B(D):
def bar1(self):
print('B.bar')
class A(B, C):
def bar1(self):
print('A.bar')
a = A()
a.bar()
######运行结果######
C:\Python27\python.exe D:/Project/Python/Pro_py2/test.py
D.bar
Process finished with exit code 0
Python 2.x示例(新式类):
class D(object):
def bar(self):
print('D.bar')
class C(D):
def bar(self):
print('C.bar')
class B(D):
def bar1(self):
print('B.bar')
class A(B, C):
def bar1(self):
print('A.bar')
a = A()
a.bar()
######运行结果######
C:\Python27\python.exe D:/Project/Python/Pro_py2/test.py
C.bar
Process finished with exit code 0
(注意:如果是在Python 3.x,上面2个代码的运行结果都是C.bar)
经典类:首先去A类中查找,如果A类中没有,则依次去B类->D类->C类中找,如果都没有找到则报错。
新式类:首先去A类中查找,如果A类中没有,则依次去B类->C类->D类中找,如果都没有找到则报错。
多态
实际上Python不支持多态,并且也用不到多态,多态的概念是应用于Java和C#这一类强类型语言中,而Python崇尚“鸭子类型”(dyck typing)。
(
在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用的。例如,在不使用鸭子类型的语言中,我们可以编写一个函数,它接受一个类型为"鸭子"的对象,并调用它的"走"和"叫"方法。在使用鸭子类型的语言中,这样的一个函数可以接受一个任意类型的对象,并调用它的"走"和"叫"方法。如果这些需要被调用的方法不存在,那么将引发一个运行时错误。任何拥有这样的正确的"走"和"叫"方法的对象都可被函数接受的这种行为引出了以上表述,这种决定类型的方式因此得名。
鸭子类型通常得益于不测试方法和函数中参数的类型,而是依赖文档、清晰的代码和测试来确保正确使用。
)
示例:
class AudioFile:
def __init__(self, filename):
if not filename.endswith(self.ext):
raise Exception("Invalid file format")
self.filename = filename
class MP3File(AudioFile):
ext = "mp3"
def play(self):
print("Playing {} as mp3".format(self.filename))
class WavFile(AudioFile):
ext = "wav"
def play(self):
print("Playing {} as wav".format(self.filename))
class OggFile(AudioFile):
ext = "ogg"
def play(self):
print("Playing {} as ogg".format(self.filename))
class FlacFile:
"""
Though FlacFile class doesn't inherit AudioFile class,
it also has the same interface as three subclass of AudioFile.
It is called duck typing.
"""
def __init__(self, filename):
if not filename.endswith(".flac"):
raise Exception("Invalid file format")
self.filename = filename
def play(self):
print("Playing {} as flac".format(self.filename))
MP3File、WavFile、OggFile三个类型继承了AudioFile这一积累,而FlacFile没有扩展AudioFile,但是可以在python中使用完全相同的接口与之交互。因为任何提供正确接口的对象都可以在python中交替使用,它减少了多态的一般超类的需求。继承仍然可以用来共享代码,但是如果所有被共享的都是公共接口,鸭子类型就是所有所需的。这减少了继承的需要,同时也减少了多重继承的需要;通常,当多重继承似乎是一个有效方案的时候,我们只需要使用鸭子类型去模拟多个超类之一(定义和那个超类一样的接口和实现)就可以了。
浙公网安备 33010602011771号