前言
上一篇文章我们讲到了面向对象编程,而面向对象编程有三大特性,分别是封装性,继承性以及多态性,这些特点呢,使得我们的程序的扩展性和健壮性变好。
继承
概念
继承,一种对类进行分层划分的概念。其基本思想是在类的基础上制定出一个新的类,而这个类呢,不仅仅可以继承原本类的属性和方法,还可以增加新的属性和方法。先前制定的类呢,就叫做父类,而后制定的类就叫做子类。一般情况下呢,一个子类是可以继承多个父类的。可是,有一些语言是不支持多重继承的,譬如Java和PHP,python是支持的。Java和PHP是有接口(interface)和抽象类(abstract)这些概念的,python也有这样的概念,但语法本身是不直接提供(例如abc模块中提供abstractmethod),这些东西呢,也不是我们今天要讨论的重点。
子类的定义
class animal:
def __init__(self,name):
self.name=name
def play(self):
print('i am',self.name)
class Dog(animal):
pass
dog=Dog("AM")
dog.play()
运行结果
i am AM
这个例子里面,在animal这个父类定义了name属性和play这个方法,但我们在Dog里面什么都没有定义。不过呢,因为Dog是由animal继承而来的,所以,Dog拥有name属性和play这个方法。类继承可以帮助我们重用方法,从而减少所需编写的代码量。
两点注意:
-
如果子类定义了构造方法,那么父类的构造方法__init__是不会被自动调用的,需要在子类的构造方法中专门调用
运行结果class animal: def __init__(self,name): self.name=name def play(self): print('i am',self.name) class Dog(animal): def __init__(self): print('AM') dog=Dog() dog.play()
AM AttributeError: 'Dog' object has no attribute 'name'
这个例子呢,说明了animal中的构造方法没有被执行。但是,我们可以使用super函数来调用。
class animal: def __init__(self,name): self.name=name def play(self): print('i am',self.name) class Dog(animal): def __init__(self): super(Dog,self).__init__("AM") dog=Dog() dog.play()
运行结果
i am AM
我们在调用animal的构造方法后运行正确
-
子类不能继承父类中的私有方法,也不能调用父类的私有方法。
class animal: def __init__(self,name): self.__name=name def __play(self): print('animal,__play') def play(self): print("animal,play") class Dog(animal): def __init__(self): super(Dog,self).__init__("AM") def say(self): self.play() self.__play() dog=Dog() dog.say()
运行结果
animal,play AttributeError: 'Dog' object has no attribute '_Dog__play'
结果说明,父类的私有方法不会被子类继承。
多态
概念
多态,顾名思义,就是多种形态,即使不知道变量所引用的对象是什么类型的,也能和对象进行操作。多态会根据类的不同而表现出不同的行为。
继承可以帮我们重复代码,但是有时候子类的行为不一定完全和父类一样。
例子
class animal:
def say(self):
print('animal')
class Dog(animal):
pass
class Cat(animal):
pass
dog=Dog()
dog.say()
cat=Cat()
cat.say()
animal
animal
在这里呢,我们是想让dog和cat输出不一样的东西的,那我们要怎么实现呢
class animal:
def say(self):
print('animal')
class Dog(animal):
def say(self):
print('dog')
class Cat(animal):
def say(self):
print("cat")
dog=Dog()
dog.say()
运行结果
dog
cat
这里呢,我们看到“Dog”和“Cat”分别各自调用了自己的方法。
当子类和父类有相同的方法时,子类就会覆盖父类的方法,这样代码在运行的时候就会调用子类的方法,这就是多态。
判断一个实例是不是某个对象可以使用isinstance函数
class animal:
def say(self):
print('animal')
class Dog(animal):
def say(self):
print('dog')
class Cat(animal):
def say(self):
print("cat")
dog=Dog()
cat=Cat()
print(isinstance(dog,Dog))
print(isinstance(dog,animal))
print(isinstance(cat,Cat))
print(isinstance(cat,animal))
print(isinstance(Dog,animal))
print(isinstance(Cat,animal))
运行结果
True
True
True
True
False
False
我们通过结果,可以看到,‘dog’不仅仅是‘Dog'的实例,也是’animal‘的实例,cat就同理了,而Dog和Cat是类,自然就不是实例了。
class animal:
def say(self):
print('animal')
class Dog(animal):
def say(self):
print('dog')
class Cat(animal):
def say(self):
print("cat")
def Animal_say(Animal:animal):
Animal.say()
dog=Dog()
cat=Cat()
Animal_say(dog)
Animal_say(cat)
dog
cat
上面这个例子告诉我们,函数Animal根本不管你是Cat的还是Dog的,只要你是animal的子类就可以了 。
鸭子类型
概念
鸭子类型(duck typing)是动态类型的一种风格。在这种风格里面,一个语法的有效语义,不是由继承自特定的类或实现特定的接口决定的,而是由当前方法和属性的集合决定的。
为什么叫鸭子类型
这里有个很生动的例子:当一只鸟走起路来像鸭子,游泳像鸭子,叫起来也像鸭子,那么这只鸟不就可以叫做鸭子吗。
在鸭子类型中,我们所关心的不是对象的类型本身,而是它如何使用。鸭子类型通常不是得益于测试方法和函数中参数的类型,而是依赖文档,代码和测试来确保其正常运行。从静态语言转向动态类语言的用户通常尝试添加一些静态的(在运行之前)类型检查,从而影响了鸭子类型的益处和可伸缩性,并约束了语言的动态特性。
鸭子类型在多态实现中的应用
class Dog:
def say(self):
print('Dog')
class Cat:
def say(self):
print("Cat")
def Animal_say(Animal):
Animal.say()
dog=Dog()
cat=Cat()
Animal_say(dog)
Animal_say(cat)
运行结果
Dog
Cat
在这个例子中,Dog和Cat并没有从animal继承,但都实现了各自的功能。这可以算是“多态”的一种实现方式。python是一个动态的语言,在函数没有被执行时,程序不可以去断定数据的类型(虽可以用函数注释来做限制,但这不是强制性的)只有执行了才去调用相关的方法。这里,我们没有使用继承,但我们仍然可以实现多态。
类变量和实例变量
类变量
一种在定义类时候定义的变量
class animal:
name="动物"
区别
类变量不需要实例化就能被调用,相当于绑定在类上,而不是绑定在实例上
class animal:
name="动物"
print(animal.name)
动物
但类变量在实例化中也是可以被调用的
class animal:
name="动物"
dog=animal()
cat=animal()
print(dog.name)
print(cat.name)
animal.name='爬行类动物'
print(dog.name)
print(cat.name)
动物
动物
爬行类动物
爬行类动物
注意:实例不可以修改类变量。如果实例对类变量进行修改,python编译器会新建一个同名的成员变量来代理实例中的类变量。
静态方法和类方法
静态方法
静态方法和类有点相似,在定义类的时候就已经被分配定义好。但静态方法不绑定类也不绑定实例,就给方法加个前缀。定义静态方法是用装饰器实现的。
定义静态方法
class animal:
name="动物"
@staticmethod
def play():
print('playing')
animal.play()
定义静态方法就是在定义函数的上面一行添加一句@staticmethod(装饰器语句)。静态方法不再有默认参数’self‘,因此静态方法本身也不能调用成员变量和成员方法。静态方法不需要实例化之后使用,和类变量一样直接使用就可以了,其他的地方和一般函数一样。
类方法
绑定在定义的类上面的方法,而不是绑定在实例上。
定义类方法
class animal:
name="动物"
@classmethod
def play(cls):
print(cls.name,'playing')
animal.play()
动物 playing
语法和静态方法差不多,但不一样的是,类方法和成员方法一样都会有一个初始的参数,这个参数又不同于成员方法。成员方法的第一个参数指的是实例,而类方法指向的是定义的类本身,所以类方法可以读取和修改类变量。
总结
我们用两篇文章对python语言中的面向对象编程进行了详细的介绍,后续我们会讲一下模块,文件与io的操作,还有一些关于python的其他有趣的知识,希望大家能关注一下博主,博主也会和大家分享所学的知识,谢谢啦!