一、继承的简介
1.引入
class Doctor():
name = ''
age = ''
def treat(self):
print('treat a patient')
class soldier():
name = ''
age = ''
def protect(self):
print('Protect the people')
以上定义了两个类,但是又重复的属性name、age,如果每定义一个类都要分别定义这两个属性,很重复冗余,所以可以抽象出一个类来封装它们的公共属性和方法,即它们的父类这些类都继承
自它们的父类。
如
class Person():
name = ''
age = ''
2.继承的特点:
1.提高了代码的复用性;
2.让类与类之间产生联系,有了这个联系,才有了多态。
继承是面向对象的三大特性之一,其他两个特性是封装、多态。
定义一个动物类
class Animal:
def run(self):
print('Running...')
def sleep(self):
print('Sleeping...')
a = Animal()
a.run()
再定义一个狗类,
class Dog:
def run(self):
print('Running...')
def sleep(self):
print('Sleeping...')
这样定义狗类,有大量重复性代码,表现较差,我们应该使狗类继承自动物类,继承动物类的属性和方法。
class Dog(Animal):
pass
d = Dog()
d.run()
d.sleep()
打印
Running...
Sleeping...
在定义类时可以在类名+括号,括号内的指定的是当前类的父类(超类、基类,super)。
class Dog(Animal):
def Housekeep(self):
print('Housekeeping...')
d = Dog()
d.run()
d.sleep()
d.Housekeep()
r1 = isinstance(d,Dog)
r2 = isinstance(d,Animal)
print(r1,r2)
打印
Running...
Sleeping...
Housekeeping...
True True
显然,d既是Dog的实例,也是Animal的实例,所以Dog也继承自了Animal。
如果在创建类的时候省略了父类,则默认父类是object;
object是所有类的父类,所有类都继承自object。
issubclass()
检查一个类是否是另一个类的子类。
class Animal:
def run(self):
print('Running...')
def sleep(self):
print('Sleeping...')
class Dog(Animal):
def Housekeep(self):
print('Housekeeping')
r1 = issubclass(Dog,Animal)
r2 = issubclass(Animal,object)
print(r1,r2)
证明了所有类都是object的子类,所有对象都是object的实例。
二、方法的重写
如果在子类中有和父类重名的方法,通过子类的实例去调用方法时,会调用子类的方法而不是父类的方法,这个特点称为方法的重写(覆盖,override)。
如,
class Animal:
def run(self):
print('Running...')
def sleep(self):
print('Sleeping...')
class Dog(Animal):
def run(self):
print('Dog is running...')
def sleep(self):
print('Dog is sleeping...')
def Housekeep(self):
print('Dog is housekeeping')
d = Dog()
d.run()
打印Dog is running...
下边再详细说明,
class A(object):
def test(self):
print('A...')
class B(A):
pass
class C(B):
pass
c = C()
c.test()
打印A...
。
类B中加入方法test()后,
class A(object):
def test(self):
print('A...')
class B(A):
def test(self):
print('B...')
class C(B):
pass
c = C()
c.test()
打印B...
类C中加入方法test()后,
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()
打印C...
。
当我们调用一个对象时,会有先去当前对象寻找是否有该方法,如果有直接调用;如果没有,则取当前对象的父类中寻找,如果有,直接调用父类中的方法,如果没有,则去父类的父类寻找,如果有直接调用,以此类推,直到找到object,如果依然没有则报错。
三、super()
父类中所有的方法都会被子类继承,包括特殊方法,如__init__()
等,也可以重写特殊方法。
class Animal:
def __init__(self,name):
self._name = name
def run(self):
print('Running...')
def sleep(self):
print('Sleeping...')
@property
def name(self):
return self._name
@name.setter
def name(self,name):
self._name = name
class Dog(Animal):
def run(self):
print('Dog is running...')
def sleep(self):
print('Dog is sleeping...')
def Housekeep(self):
print('Dog is housekeeping')
d = Dog()
d.run()
会抛出异常,TypeError: __init__() missing 1 required positional argument: 'name'
因为初始化没有传入参数,因为父类初始化要传入name这个参数,子类也必须传入。
传入参数后,
class Animal:
def __init__(self,name):
self._name = name
def run(self):
print('Running...')
def sleep(self):
print('Sleeping...')
@property
def name(self):
return self._name
@name.setter
def name(self,name):
self._name = name
class Dog(Animal):
def run(self):
print('Dog is running...')
def sleep(self):
print('Dog is sleeping...')
def Housekeep(self):
print('Dog is housekeeping')
d = Dog('erha')
print(d.name)
打印erha
调用setter方法修改属性,
class Animal:
def __init__(self,name):
self._name = name
def run(self):
print('Running...')
def sleep(self):
print('Sleeping...')
@property
def name(self):
return self._name
@name.setter
def name(self,name):
self._name = name
class Dog(Animal):
def run(self):
print('Dog is running...')
def sleep(self):
print('Dog is sleeping...')
def Housekeep(self):
print('Dog is housekeeping')
d = Dog('erha')
d.name = 'demu'
print(d.name)
打印demu
。
如果需要在子类中定义新的属性时,即要扩展属性时,
class Animal:
def __init__(self,name):
self._name = name
def run(self):
print('Running...')
def sleep(self):
print('Sleeping...')
@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('Dog is running...')
def sleep(self):
print('Dog is sleeping...')
def Housekeep(self):
print('Dog is housekeeping')
@property
def age(self):
return self._age
@age.setter
def age(self, age):
self._age = age
d = Dog('erha',10)
print(d.name)
print(d.age)
打印
erha
10
可以直接调用父类中的__init__()
来初始化父类中的属性,
class Animal:
def __init__(self,name):
self._name = name
def run(self):
print('Running...')
def sleep(self):
print('Sleeping...')
@property
def name(self):
return self._name
@name.setter
def name(self,name):
self._name = name
class Dog(Animal):
def __init__(self,name,age):
Animal.__init__(self,name)
self._age = age
def run(self):
print('Dog is running...')
def sleep(self):
print('Dog is sleeping...')
def Housekeep(self):
print('Dog is housekeeping')
@property
def age(self):
return self._age
@age.setter
def age(self, age):
self._age = age
d = Dog('erha',10)
print(d.name)
print(d.age)
即Dog类中的初始化方法中,name属性通过Animal的初始化调用获取,可以将父类的多个属性传给子类,但是继承的父类是固定的,这是需要用suoer()动态地继承父类属性。
super()可以用来获取当前类的父类,并且通过suoer()返回的对象,调用父类方法时不需要传入self。
class Animal:
def __init__(self,name):
self._name = name
def run(self):
print('Running...')
def sleep(self):
print('Sleeping...')
@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().__init__(name)
self._age = age
def run(self):
print('Dog is running...')
def sleep(self):
print('Dog is sleeping...')
def Housekeep(self):
print('Dog is housekeeping')
@property
def age(self):
return self._age
@age.setter
def age(self, age):
self._age = age
d = Dog('erha',10)
print(d.name)
print(d.age)
执行结果与之前相同。
四、多重继承
语法:类名._bases_
可以用来获取当前类的所有直接父类。
class A(object):
def test(self):
print('A...')
class B(A):
def test2(self):
print('B...')
class C(B):
pass
print(C.__bases__)
print(B.__bases__)
print(A.__bases__)
打印
(<class '__main__.B'>,)
(<class '__main__.A'>,)
(<class 'object'>,)
在Python中支持多重继承,即可以为一个类同时指定多个父类。
class A(object):
def test(self):
print('A...')
class B(object):
def test2(self):
print('B...')
class C(A,B):
pass
print(C.__bases__)
打印(<class '__main__.A'>, <class '__main__.B'>)
。
class A(object):
def test(self):
print('A...')
class B(object):
def test2(self):
print('B...')
class C(A,B):
pass
c = C()
c.test()
c.test2()
打印
A...
B...
显然,子类可以同时继承多个父类的方法。
但是一般在实际中没有特殊情况,尽量避免使用多重继承,因为多重继承会让代码过于复杂;
如果多个父类中有同名的方法,则会在第一个父类中寻找,找不到再继续往下寻找…
如,
class A(object):
def test(self):
print('A...test')
class B(object):
def test(self):
print('B...test')
def test2(self):
print('B...')
class C(A,B):
pass
c = C()
c.test()
会打印A...test
如果A中没有test()方法,
class A(object):
pass
class B(object):
def test(self):
print('B...test')
def test2(self):
print('B...')
class C(A,B):
pass
c = C()
c.test()
会打印B...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('Hello,%s' % obj.name)
a = A('Tom')
b = B('Jerry')
speak(a)
speak(b)
打印
Hello,Tom
Hello,Jerry
定义一个新类C,但是没有name属性时,
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
class C(object):
pass
def speak(obj):
print('Hello,%s' % obj.name)
a = A('Tom')
b = B('Jerry')
c= C()
speak(a)
speak(b)
speak(c)
会抛出异常,AttributeError: 'C' object has no attribute 'name'
,
增加类型检查,
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
class C(object):
pass
def speak(obj):
print('Hello,%s' % obj.name)
def test_speak(obj):
'''类型检查'''
if isinstance(obj,A):
print('Hello,%s' % obj.name)
a = A('Tom')
b = B('Jerry')
c= C()
test_speak(a)
test_speak(b)
test_speak(c)
打印Hello,Tom
。
在test_speak()
这个函数中做了类型检查,也就是obj是A类型的对象的时候,才可以正常执行,其他类型的对象无法使用该函数。
这个函数其实就是违反了多态。
违反了多态的函数,只适合于一种类型的对象,无法适用于其他类型的对象,导致函数的适用性非常差。
多态使面向对象的编程更加具有灵活性
。
l = [1,2,3]
s = 'python'
print(len(l),len(s))
如上,len()函数既可以得到list的长度,又可以获取str的长度,
len()就可以检查不同对象类型的长度就是面向对象的特征之一——多态。
len()的底层实现:
之所以len()函数能获取长度,是因为这些对象中有一个特殊方法__len__()方法;
换句话说,只要对象中有__len__()方法,就可以通过len()方法来获取对象的长度。
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
b = B('Jerry')
print(len(b))
会报错TypeError: object of type 'B' has no len()
。
在B中加入__len__()
方法后,
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 __len__(self):
return 1
b = B('Jerry')
print(len(b))
会打印1
面向对象的三大特征:
1.封装:确保对象中的数据更安全;
2.继承:保证对象的可扩展性;
3.多态:保证了程序的灵活性。
六、类中的属性和方法
类属性是直接在类中定义的属性,类属性可以通过类或类的实例访问;
通过实例对象添加的属性属于实例属性。
class A(object):
#类属性
count = 0
a = A()
print(A.count)
print(a.count)
打印
0
0
修改实例的属性,
class A(object):
#类属性
count = 0
a = A()
a.count = 5
print('In class,',A.count)
print('In instance,',a.count)
打印
In class, 0
In instance, 5
修改类中的属性,
class A(object):
#类属性
count = 0
a = A()
A.count = 5
print('In class,',A.count)
print('In instance,',a.count)
打印
In class, 5
In instance, 5
可总结出:类属性只能通过类对象来修改,无法通过实例对象来修改;
class A(object):
def __init__(self):
#实例属性
self.name = 'Tom'
a = A()
print(a.name)
打印Tom
在初始化中定义的属性为实例属性,只能通过实例属性来访问和修改,类对象无法访问和修改。
如,
class A(object):
def __init__(self):
#实例属性
self.name = 'Tom'
a = A()
print(A.name)
报错AttributeError: type object 'A' has no attribute 'name'
在类中定义的以self为第一个参数的方法都是实例方法;
实力方法在调用时,Python会将调用对象作为self传入;
class A(object):
def __init__(self):
#实例属性
self.name = 'Tom'
def test(self):
#实例方法
print('in test')
a = A()
a.test()
打印in test
类对象调用时,
class A(object):
def __init__(self):
#实例属性
self.name = 'Tom'
def test(self):
#实例方法
print('in test')
a = A()
A.test()
报错TypeError: test() missing 1 required positional argument: 'self'
但是通过类对象调用时,传入实例对象多为方法的参数不会报错。
即通过类对象调用方法时,不会自动穿self,必须手动传self。
class A(object):
def __init__(self):
#实例属性
self.name = 'Tom'
def test(self):
#实例方法
print('in test')
a = A()
A.test(a)
输出结果相同。
即a.test()
等价于A.test(a)
。
可以通过classmethod装饰器来调用类方法;
类方法的第一个参数是cls,也会被自动传递,cls就是当前的类对象。
class A(object):
count = 0
def __init__(self):
#实例属性
self.name = 'Tom'
def test(self):
#实例方法
print('in test')
@classmethod
def test2(cls):
#实例方法
print('in test2',cls)
a = A()
A.test2()
打印in test2 <class '__main__.A'>
调用类属性,
class A(object):
count = 0
def __init__(self):
#实例属性
self.name = 'Tom'
def test(self):
#实例方法
print('in test')
@classmethod
def test2(cls):
#实例方法
print('in test2',cls)
print(cls.count)
a = A()
A.test2()
打印
in test2 <class '__main__.A'>
0
通过实例调用类方法,
class A(object):
count = 0
def __init__(self):
#实例属性
self.name = 'Tom'
def test(self):
#实例方法
print('in test')
@classmethod
def test2(cls):
#实例方法
print('in test2',cls)
print(cls.count)
a = A()
a.test2()
结果相同。
可总结:类方法可以通过类对象调用,也可通过实例方法调用。
A.test2()
等价于a.test2()
。
静态方法:
用staticmethod装饰器来调用类方法
class A(object):
count = 0
def __init__(self):
#实例属性
self.name = 'Tom'
def test(self):
#实例方法
print('in test')
@classmethod
def test2(cls):
#实例方法
print('in test2',cls)
print(cls.count)
@staticmethod
def test3():
print('in test3')
a = A()
A.test3()
a.test3()
打印
in test3
in test3
静态方法,基本上是一个和当前类无关的方法,它只是一个保存到当前类中的函数;
静态方法一般是工具方法,和当前类无关。
大家也可以关注我的公众号:Python极客社区,在我的公众号里,经常会分享很多Python的文章,而且也分享了很多工具、学习资源等。另外回复“电子书”还可以获取十本我精心收集的Python电子书。