12. 继承
1. 继承简介
• 继承是面向对象三大特性之一
• 通过继承我们可以使一个类获取到其他类中的属性和方法
• 在定义类时,可以在类名后面的括号中指定当前类的父类(超类、基类)
• 继承提高了类的复用性。让类与类之间产生了关系。有了这个关系,才有了多态的特性
• 任何类的最大的父类是object类,所有的特殊方法都是被写到object类里
建立任何类的时候,无论输入的括号中是否有object都默认建立的类的父类是object
class Animal(object):
# def __init__(self, name):
# self.__name = name
def sleep(self):
print('动物会睡觉')
def run(self):
print('动物会跑')
class Dog(Animal):
# 括号里的类就是当前类的父类(超类,基类)
pass
class Cat(Animal):
def run(self):
print('猫会跑')
dog = Dog()
dog.run()
这里的Dog()会继承Animal()类的属性和方法,
而Cat()中的定义方法就是在修改其从其父类Animal()那继承的方法。
• 但如果父类需要传递参数,那么继承父类以后的类,也需要传递参数。
class Animal(object):
def __init__(self, name):
self.__name = name
def sleep(self):
print('动物会睡觉')
def run(self):
print('动物会跑')
class Dog(Animal):
# 括号里的类就是当前类的父类(超类,基类)
pass
dog = Dog(‘哈士奇’)
dog.run()
• 通过issubclass() 检测一个类是否是一个类的父类,()内前者是主对象,后者是需要判断的父类
print(issubclass(Dog, Animal))
print(issubclass(Dog, object))
2. 方法重写
• 如果在子类中有和父类同名的方法,则通过子类实例去调用方法时,会调用子类的方法而不是父类的方法,这个特点我们称之为方法的重写(覆盖)
• 当我们调用一个对象的方法时:
• 会优先去当前对象中寻找是否具有该方法,如果有则直接调用
• 如果没有,则去当前对象的父类中寻找,如果父类中有则直接调用父类中的方法
• 如果没有,则去父类中的父类寻找,以此类推,直到找到object,如果依然没有找到就报错了
3. super()
• super()可以获取当前类的父类
• 并且通过super()返回对象调用父类方法时,不需要传递self
4. 多重继承
• 在Python中是支持多重继承的。也就是我们可以为一个类同时制定多个父类
• 可以在类名的()后边添加多个类,来实现多重继承
• 多重继承,会使子类同时拥有多个父类,并且会获取到所有父类中的方法
• 在开发中没有特殊情况,应该尽量避免使用多重继承。因为多重继承会让我们的代码更加复杂
• 如果多个父类中有同名的方法,则会先在第一个父类中寻找,然后找第二个,找第三个…前面会覆盖后面的
class Animal(object):
def sleep(self):
print('动物会睡觉')
def run(self):
print('动物会跑')
class Dog(Animal): # 括号里的类就是当前类的父类(超类,基类)
def run(self):
Animal.run(self)
print('狗会跑')
dog = Dog()
dog.run()
dog.sleep()
>狗会跑
>动物会睡觉
3. super()
• super()可以获取当前类的父类
• 并且通过super()返回对象调用父类方法时,不需要传递self
class Animal(object):
def sleep(self):
print('动物会睡觉')
def run(self):
print('动物会跑')
class Dog(Animal): # 括号里的类就是当前类的父类(超类,基类)
def run(self):
Super().run()
# super(Dog, self).run()
Animal.run(self)
print('狗会跑')
dog = Dog()
dog.run()
dog.sleep()
>动物会跑
狗会跑
动物会睡觉
4. 多重继承
• 在Python中是支持多重继承的。也就是我们可以为一个类同时制定多个父类
• 可以在类名的()后边添加多个类,来实现多重继承
• 多重继承,会使子类同时拥有多个父类,并且会获取到所有父类中的方法
• 在开发中没有特殊情况,应该尽量避免使用多重继承。因为多重继承会让我们的代码更加复杂
• 如果多个父类中有同名的方法,则会先在第一个父类中寻找,然后找第二个,找第三个…前面会覆盖后面的
class A(object):
def text(self):
print('A')
class B(object):
def text(self):
print('B')
def text2(self):
print('text2')
class C(A, B):
pass
c = C()
c.text()
c.text2()
print(C.__bases__)
>A
>text2
>(<class '__main__.A'>, <class '__main__.B'>)
5. 多态
• 面向对象三大特性
- 封装 确保对象中数据的安全
- 继承 保证了对象的扩展性
- 多态 保证了程序的灵活性
• 多态是面向对象的三大特性之一。从字面理解就是多种形态
• 一个对象可以以不同形态去呈现
• Python中多态的特点
1wo. 只关心对象的实例方法是否同名,不关心对象所属的类型;
5. 对象所属的类之间,继承关系可有可无;
6. 多态的好处可以增加代码的外部调用灵活度,让代码更加通用,兼容性比较强;
7. 多态是调用方法的技巧,不会影响到类的内部设计。
6. 属性和方法
1.属性
- 类属性,直接在类中定义的属性是类属性
• 类属性可以通过类或类的实例访问到。但是类属性只能通过类对象来修改,无法通过实例对象修改 - 实例属性 通过实例对象添加的属性属于实例属性
• 实例属性只能通过实例对象来访问和修改,类对象无法访问修改
class A(object):
# 类属性 直接定义在类中的属性
# 类属性可以通过类和该类的实例来访问
# 类属性只能通过类对象(A)来修改,无法通过实例对象来修改
# num = 0
def __init__(self, num):
self.num = num
#这里则是通过方法直接定义为实例属性
def text(self):
print('text实例方法')
a = A(10)
print(a.num)
a.num = 5
print(a.num)
# 如果执行print(A.num)就会报错
>10
>5
2.方法
- 实例方法
• 在类中定义,以self为第一个参数的方法都是实例方法
• 实例方法在调用时,Python会将调用对象以self传入
• 实例方法可以通过类实例和类去调用
• 当通过实例调用时,会自动将当前调用对象作为self传入
• 当通过类调用时,不会自动传递实例(self),我们必须手动传递实例(self)
class A(object):
def text(self):
print('text实例方法')
a = A()
a.text()
A.text(a)
>text实例方法
>text实例方法
- 类方法 在类的内容以@classmethod 来修饰的方法属性类方法
• 类方法第一个参数是cls 也会自动被传递。cls就是当前的类对象
• 类方法和实例方法的区别,实例方法的第一个参数是self,类方法的第一个参数是cls
• 类方法可以通过类去调用,也可以通过实例调用 - 静态方法
• 在类中用@staticmethod来修饰的方法属于静态方法
• 静态方法不需要指定任何的默认参数,静态方法可以通过类和实例调用
• 静态方法,基本上是一个和当前类无关的方法,它只是一个保存到当前类中的函数
• 静态方法一般都是些工具方法,和当前类无关,用法与工具函数类似
class A(object):
def text(self):
print('text实例方法')
#@classmethod
def text1(cls):
print('这是text1类方法')
#@staticmethod
def text2():
print('这是text2静态方法')
a = A()
# 实例方法
a.text()
A.text(a)
# 类方法
# 类方法可以通过类对象调用也可以通过实例对象调用
# cls是第一个参数,也是当前的类对象
a.text1()
A.text1()
# 总结:类当中所拥有的的一切,实例对象都是可以使用的
# 静态方法 在类中用@staticmethod装饰的就是静态方法
# 静态方法和这个类没有关系,不需要指定任何参数,实例对象和类对象都是可以直接调用静态方法,静态方法就相当于一个功能函数,只是存放在了类中而已
a.text2()
A.text2()
7. 单例模式
1.new()方法
new()方法用于创建与返回一个对象。在类准备将自身实例化时调用。
new()方法用于创建对象
class Demo(object):
def __init__(self):
print("__init__")
def __new__(cls):
print("__new__")
d = Demo()
>__new__
如上面这个代码执行时只会打印出__new__
- 解释
此处的new方法和init方法皆是object类中的特殊函数
• new()方法用于创建对象,但是要将类作为参数传递
• init()方法在对象创建的时候,自动调用
• 但是此处重写了父类的__new__()方法,覆盖了父类__new__()创建对象的功能,所以对象并没有创建成功。所以仅执行修改后的__new__()方法内部代码,无法执行init方法 - 对象创建执行顺序
• 1.通过__new__()方法创建对象
• 2.并将对象返回,传给__init__() - 因此如果要使init方法也能执行,就是在自定义类中实现创建对象,应当做一下修改
思路:
1.重写父类__new__()方法
2.并且在该方法内部,调用父类的__new__()方法
class Demo(object):
def __init__(self):
print("__init__")
def __new__(cls):
print("__new__")
return super().__new__(cls)
d = Demo()
>__new__
>__init__
注意
• 在创建对象时,一定要将对象返回,才会自动触发__init__()方法
• init()方法当中的self,实际上就是__new__返回的实例,也就是该对象
init()与__new__()区别
• __init__是实例方法,__new__是静态方法
• __init__在对象创建后会被自动调用
2. 单例模式介绍
单例模式是一种常用的软件设计模式。也就是说该类只包含一个实例。
通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。
通常应用在一些资源管理器中,比如日志记录等。
单例模式实现
3. 思路
• 当对象不存在时,创建对象
• 当对象存在时,永远返回当前已经创建对象
class single(object):
__isinstance = None
def __new__(cls, *args, **kwargs):
if cls.__isinstance is None:
cls.__isinstance = super().__new__(cls)
return cls.__isinstance
else:
return cls.__isinstance
a = single()
b = single()
print(id(a))
print(id(b))
>3011332690608
>3011332690608
解释:按照思路所述,一个类只能有一个实例,而且实例永远是同一个(即id是一样的),这样就可以减少其使用内存。因此这里的代码对object类中建立实例的new方法进行了重写,先是进行判断是否存在实例,如果不存在实例,则仍需要调用原有的new方法进行实例的创建;如果已经存在实例,那么所需要做的就是返回到已经创建的那个实例。在这里可以把定义类单独看为一个代码块,而属性就是变量,方法则是函数,而魔法方法则是已经有的函数。