一、继承的简介
1.1 继承的引入
class Person():
name = ' '
age =
class Doctor():
name = ' '
age = ' '
def treat(self):
print('治疗病人...')
class Soldier():
name = ' '
age =
def duty(self):
print('保卫国家...')
这两个类中有公共的属性name和age,我们是否可以将这两个共有属性抽象成一个单独类Person,然后让这三个类之间存在一些关联,这个过程就叫做继承。
1.2 继承的优点
1.提高了代码的复用性
2.让类与类之间产生了关系,有了这个关系,才有多态
1.3 继承的应用
定义一个狗类,有以下三种方法:
1.直接修改动物类, 修改起来比较麻烦且会违反OCP
2.直接定义新的狗类,新类比较麻烦,会出现大量的复制粘贴代码,维护起来会比较困难
3.直接从Animal这个类中继承它的属性和方法
class Animal():
def run(self):
print('动物跑.....')
def sleep(self):
print('动物睡觉...')
a = Animal()
a.run()
class Dog(Animal):
def home(self):
print('狗看家')
d = Dog()
d.run() #调用父类Animal的方法run
在定义类时,可以在类名后加上一个括号,括号中指定当前类的父类(超类、基类、super)
r = isinstance(d,Dog)
r1 = isinstance(d,Animal)
print(r) #返回值为True,d是Dog的实例对象
print(r1) #返回值为True,d也是父类Animal的实例对象
如果我们在创建类时,没有指定父类,则默认该类的父类为object
object是所有类的父类
r = issubclass(Dog,Animal) #检查Dog是否是Animal的子类
r1 = issubclass(Animal,object) #检查Animal是否是object的子类
r2 = issubclass(Dog,object) #检查Dog是否是object的子类
print(r) #返回结果为True
print(r1) #返回结果为True
print(r2) #返回结果为True
二、方法的重写
如果在子类中有和父类重名的方法,我们通过子类的实例对象去调用该方法时,会调用子类的方法而不是父类的方法,这种特性称为方法的重写或者覆盖(override)
class A(object):
def test(self):
print('A....')
class B(A):
def test(self):
print('B....')
class C(B):
def test(self):
print('C....')
c = C()
c.test()
当我们调用一个对象时,
会优先去当前对象寻找是否有该方法,如果有直接调用
如果没有,则去当前对象的父类对象中寻找,如果有则调用父类的该方法
如果没有,则去父类的父类中去寻找,如果有则直接调用。以此类推,知道找到object,如果依然没有就报错。
三、super()
子类可以继承父类所有的方法和属性,包括特殊方法,也可以重写特殊方法
class Animal():
def __init__(self,name):
self._name = name
def run(self):
print('动物跑.....')
def sleep(self):
print('动物睡觉...')
@property
def name(self):
return self._name
@name.setter
def name(self,name):
self._name = name
class Dog(Animal):
def __init__(self,name,age):
self._name = name
self._age = age
def run(self):
print('狗跑')
def home(self):
print('狗看家')
@property
def age(self):
return self._age
@age.setter
def age(self,age):
self._age = age
d = Dog('二哈',5)
print(d.name)
print(d.age)
d.name = '凯撒'
d.age = 10
print(d.name)
print(d.age)
在父类中使用__init__特殊方法初始化了实例对象d的一个属性值name,但在子类中,又增加了一个属性age,如果我们在子类的初始化方法中只添加age这个属性,那么实例对象只会调用子类中的初始化方法,即d中只有一个属性age
因此,我们需要在子类对象的初始化方法中添加name和age两个属性
如果父类中的属性有多个,我们采用上述的方法对子类进行重写时,会比较麻烦。希望可以在子类中直接调用父类的__init__方法来初始化子类的属性
class Animal():
def __init__(self,name):
self._name = name
def run(self):
print('动物跑.....')
def sleep(self):
print('动物睡觉...')
@property
def name(self):
return self._name
@name.setter
def name(self,name):
self._name = name
class Dog(Animal):
def __init__(self,name,age):
#直接调用父类的init方法,需要添加self参数
Animal.__init__(self,name)
self._age = age
def run(self):
print('狗跑')
def home(self):
print('狗看家')
@property
def age(self):
return self._age
@age.setter
def age(self,age):
self._age = age
d = Dog('二哈',5)
print(d.name)
print(d.age)
d.name = '凯撒'
d.age = 10
print(d.name)
print(d.age)
我们通过该方法来实现子类的初始化时,父类名是固定的。有没有什么方法可以获得子类所指定的父类名,从而使程序更加完善。
super()可以获得子类所指定的父类名,且通过super()返回的对象调用初始化方法时,不需要传递self参数
class Animal():
def __init__(self,name):
self._name = name
def run(self):
print('动物跑.....')
def sleep(self):
print('动物睡觉...')
@property
def name(self):
return self._name
@name.setter
def name(self,name):
self._name = name
class Dog(Animal):
def __init__(self,name,age):
#super()获取子类所指定的父类名
super().__init__(name)
self._age = age
def run(self):
print('狗跑')
def home(self):
print('狗看家')
@property
def age(self):
return self._age
@age.setter
def age(self,age):
self._age = age
d = Dog('二哈',5)
print(d.name)
print(d.age)
d.name = '凯撒'
d.age = 10
print(d.name)
print(d.age)
四、多重继承
语法: 类名.__ bases__ 可以获取当前类的所有父类
在Python中是支持多重继承的,也就是我们可以为一个类指定多个父类
class A(object):
def test(self):
print('A....')
class B(object):
def test(self):
print('B....')
class C(A,B):
def test(self):
print('C....')
# c = C()
print(C.__bases__) #返回结果为元组,元组中元素就是该类的父类
在多重继承中,如果多个父类中有相同的方法,子类在调用父类的方法时,会根据子类传递的顺序进行调用
在该例子中,class C(A,B) C类有两个父类A,B,且A在B的前面,c.test()会先调用A中的test方法,如果A中没有test方法才会调用B中的test方法
class A(object):
def test(self):
print('A....')
class B(object):
def test(self):
print('B....')
class C(A,B):
#def test(self):
# print('C....')
pass
c = C()
#print(C.__bases__)
c.test()
在开发中没有特殊情况,不建议使用多重继承,多重继承会让代码过于复杂
五、多态
多态是面向对象的三大特征之一
多态字面上理解就是多种形态(狗(二哈、萨摩耶、德牧…))
一个对象可以以不同形态去展现
class A(object):
def __init__(self,name):
self._name = name
@property
def name(self):
return self._name
@name.setter
def name(self,name):
self._name = name
class B(object):
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
@name.setter
def name(self, name):
self._name = name
def speak(obj):
print('你好,我是%s' %obj.name)
def speak2(obj):
if isinstance(obj,A):
print('你好,我是%s' %obj.name)
a = A('葫芦娃')
b = B('蜘蛛侠')
speak(a)
speak(b)
在上述的例子中,a和b都可以调用speak函数,因为a和b都有name属性,这个特性就称为多态.如果我们将speak()函数修改为speak2()这个函数,那么就只有A类型的对象能调用该函数,这个函数就违反了多态。
违反了多态的函数,只适用于一种类型的对象,这样导致函数的适用性非常差。
len()函数可以检查字符串的长度,也可以检查列表的长度,这就是多态
之所以len()可以获取对象的长度,是因为这些对象中都封装了__len__特殊方法,就可以通过len()获取长度
面向对象的三大特征
封装 确保对象中的数据更安全
继承 保证了对象的可扩展性
多态 保证了程序的灵活性
六、类中的属性和方法
定义在类中的属性都是类属性,类对象中的属性值只能通过类对象来修改
class A(object):
#定义在类中的属性就是类属性,类对象和实例对象都可以访问
count = 0
a = A()
#修改的是实例对象中的count属性值
a.count = 5
#修改的是类对象中的count属性值,类对象和实例对象中的count属性都会改变
A.count = 8
print('A',A.count)
print('a',a.count)
实例属性是通过实例对象添加的属性,实例属性只能通过实例对象来访问和修改
class A(object):
# count = 0
def __init__(self,name):
#实例属性,通过实例对象添加的属性
self.name = name
a = A('葫芦娃')
a.name = '钢铁侠'
# print(A.name) #报错
print(a.name)
实例方法在类中直接定义,以self为第一个参数的都是实例方法
通过实例对象调用时,Python会将调用对象作为self传入
通过类对象调用时,不会自动传入self,需手动传入self
class A(object):
#实例方法在类中直接定义,以self作为第一个参数
def test(self):
print('这是test方法')
a = A()
a.test() #通过实例对象调用实例方法
A.test(a) #通过类对象调用实例方法,需传入调用的实例对象作为参数
a.test()等价于A.test(a)
类方法在类中定义需要使用@classmethod装饰器进行装饰
类方法的第一个参数为cls,会在调用时将当前的类对象作为cls自动传入
类方法可以通过类对象调用也可以用实例对象调用
class A(object):
#类方法在类中定义,需要用classmethod进行装饰,且以cls作为第一个参数
@classmethod
def test1(cls):
print('这是test1方法.....')
a = A()
A.test1() #通过类对象调用类方法
a.test1() #通过实例对象调用类方法
A.test1()等价于a.test1()
静态方法
静态方法,基本上一个与与当前类无关的方法,它只是一个保存在当前类中的函数
静态方法一般都是些工具方法,与当前类无关
静态方法必须用一个对象去调用它,函数可以直接使用
class A(object):
#静态方法在类中定义,需要用staticmethod进行装饰
@staticmethod
def test2():
print('这是test2方法.....')
a = A()
A.test2() #通过类对象调用静态方法
a.test2() #通过实例对象调用静态方法