本文介绍了Python的继承、方法重写、多态、属性和方法、垃圾回收、魔术方法。也是Python系列文章的第6篇。
系列文章
【Python 基础】一文补齐Python基础知识
【趣学Python:B站四大恶人】一文掌握列表、元组、字典、集合
【Python进阶】一文掌握Python函数用法
【Python进阶】Python面向对象之类与对象详解
【Python进阶】Python面向对象之装饰器与封装
【Python进阶】Python面向对象之继承和多态详解
【Python进阶】Python异常处理和模块详解
【Python进阶】Python文件(I/O)操作详解
# 定义一个类 Animal(动物)
# 这个类中需要两个方法:run() sleep()
class Animal:
def run(self):
print('动物会跑~~~')
def sleep(self):
print('动物睡觉~~~')
假设我们需要狗叫的方法,那么上边这个类能够实现我们需要的大部分功能,但是不能实现全部功能。如何让这个类来实现全部的功能呢?
1.直接修改这个类,在这个类中添加我们需要的功能;但是修改起来会比较麻烦,并且会违反OCP原则(对扩展是开放的,对修改是关闭的);
# 定义一个类 Dog(狗)
# 这个类中需要三个方法:run() sleep() bark()
class Dog:
def run(self):
print('狗会跑~~~')
def sleep(self):
print('狗睡觉~~~')
def bark(self):
print('汪汪汪~~~')
2.以上代码,通过创建一个新的类解决了添加狗叫功能的问题,但是直接创建一个新的类会有问题:
3.直接从Animal类中来继承它的属性和方法。
通过继承可以直接让子类获取到父类的方法或属性,避免编写重复性的代码,并且也符合OCP原则,所以我们经常需要通过继承来对一个类进行扩展。
继承实例:
class Dog(Animal):
def bark(self):
print('汪汪汪~~~')
d = Dog()
r = isinstance(d , Dog)
print(r)
r = isinstance(d , Animal)
print(r)
输出:
True
True
class Person(object):
pass
print(issubclass(Animal , Dog))
print(issubclass(Animal , object))
print(issubclass(Person , object))
issubclass()
检查一个类是否是另一个类的子类;isinstance()
用来检查一个对象是否是一个类的实例;True
;print(isinstance(print , object))
如果父类与子类都有名称相同的方法,在调用方法的时候,会调用哪一个?
class Animal:
def run(self):
print('动物会跑~~~')
def sleep(self):
print('动物睡觉~~~')
class Dog(Animal):
def bark(self):
print('汪汪汪~~~')
def run(self):
print('狗跑~~~~')
d = Dog()
d.run()
# 输出:狗跑~~~~ (方法重写)
class A(object):
def test(self):
print('AAA')
class B(A):
def test(self):
print('BBB')
class C(B):
def test(self):
print('CCC')
# 创建一个c的实例
c = C()
c.test()
当我们调用一个对象的方法时,会优先去当前对象中寻找是否具有该方法,如果有则直接调用;如果没有,则去当前对象的父类中寻找,如果父类中有则直接调用父类中的方法;如果没有,则去父类的父类中寻找,以此类推,直到找到object,如果依然没有找到,则报错。
父类中的所有方法都会被子类继承,包括特殊方法,也可以重写特殊方法;
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):
super().__init__(name)
def bark(self):
print('汪汪汪~~~')
def run(self):
print('狗跑~~~~')
# d = Dog() #报错:TypeError: __init__() missing 1 required positional argument: 'name'
d = Dog('旺财')
子类继承父类,如果不使用super(),父类的属性需要在子类的__init__
中初始化参数,或者直接在__init__
中继承调用的父类(Animal.__init__(self,name)
),但是这样会有问题,即继承的父类Animal
是写死的,不是动态获取的,不方便维护。
class Dog(Animal):
def __init__(self,name,age):
self._name = name
self._age = age
def bark(self):
print('汪汪汪~~~')
def run(self):
print('狗跑~~~~')
@property
def age(self):
return self._age
@age.setter
def age(self,age):
self._age = name
d = Dog('旺财',18)
print(d.name,d.age)
d.name = '蛋挞'
print(d.name,d.age)
为了解决上述问题,使用 super()
,可以获取当前的父类,并且通过 super()
返回对象调用父类方法时,不需要传递 self
。
class Dog(Animal):
def __init__(self,name,age):
# 希望可以直接调用父类的__init__来初始化父类中定义的属性
# super() 可以用来获取当前类的父类,
# 并且通过super()返回对象调用父类方法时,不需要传递self
super().__init__(name)
self._age = age
def bark(self):
print('汪汪汪~~~')
def run(self):
print('狗跑~~~~')
@property
def age(self):
return self._age
@age.setter
def age(self,age):
self._age = name
d = Dog('旺财',18)
print(d.name, d.age)
class A(object):
def test(self):
print('正在调用A中的test()方法~')
class B(object):
def test2(self):
print('正在调用B中的test2()方法~')
class C(B):
pass
print(B.__bases__)
print(C.__bases__)
class C(A, B):
pass
c = C()
c.test()
c.test2()
# 输出:
# 正在调用A中的test()方法~
# 正在调用B中的test2()方法~
如果多个父类中有同名的方法,则会现在第一个父类中寻找,然后找第二个,然后找第三个…前边父类的方法会覆盖后边父类的方法。
# 定义两个类
class A:
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:
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:
pass
调用:
a = A('孙悟空')
b = B('猪八戒')
c = C()
def say_hello(obj):
print('你好 %s'%obj.name)
say_hello(a) #-->你好 孙悟空
say_hello(b) #-->你好 猪八戒
say_hello(c) #-->AttributeError: 'C' object has no attribute 'name'
对于 say_hello()
这个函数来说,只要对象中含有 name
属性,它就可以作为参数传递,这个函数并不会考虑对象的类型,只要有 name
属性即可。
def say_hello_2(obj):
# 做类型检查
if isinstance(obj , A):
print('你好 %s'%obj.name)
say_hello(b) #-->你好 猪八戒
say_hello_2(b) #-->没有输出
say_hello_2
中我们做了一个类型检查,也就是只有 obj
是A类型的对象时,才可以正常使用,其他类型的对象都无法使用该函数,这个函数就违反了多态。违反了多态的函数,只适用于一种类型的对象,无法处理其他类型对象,这样导致函数的适应性非常的差。注意,像 isinstance()
这种函数,在开发中一般是不会使用的!len()
之所以一个对象能通过len()来获取长度,是因为对象中具有一个特殊方法__len__
;换句话说,只要对象中具有 __len__
特殊方法,就可以通过 len()
来获取它的长度。
class A:
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:
def __init__(self,name):
self._name = name
def __len__(self):
return 10
@property
def name(self):
return self._name
@name.setter
def name(self,name):
self._name = name
class C:
pass
a = A('孙悟空')
b = B('猪八戒')
c = C()
print(len(b)) #-->10
print(len(c)) #-->TypeError: object of type 'C' has no len()
面向对象的三大特征:
封装
继承
多态
# 定义一个类
class A(object):
# 类属性,直接在类中定义的属性是类属性
# 类属性可以通过类或类的实例访问到
# 但是类属性只能通过类对象来修改,无法通过实例对象修改
count = 0
def __init__(self):
# 实例属性,通过实例对象添加的属性属于实例属性
# 实例属性只能通过实例对象来访问和修改,类对象无法访问修改
self.name = '孙悟空'
a = A()
# 实例属性,通过实例对象添加的属性属于实例属性
a.count = 10
A.count = 100
print('a_count:', a.count) #-->
print('A_count:', A.count) #-->
print('a_name:', a.name) #-->
print('A_name', A.name) #-->AttributeError: type object 'A' has no attribute 'name'
在类中定义,以 self
为第一个参数的方法都是实例方法;
实例方法在调用时,Python会将调用对象作为 self
传入;
实例方法可以通过实例和类去调用;
self
传入;class A(object):
def __init__(self):
self.name = '孙悟空'
def test(self):
print('这是test方法~~~ ', self)
a = A()
a.test() #-->这是test方法~~~ <__main__.A object at 0x000001BB04D7D5C8>
A.test(a) #-->这是test方法~~~ <__main__.A object at 0x000001BB04D7D5C8>
@classmethod
来修饰的方法属于类方法;cls
,也会被自动传递,cls
就是当前的类对象;self
,而类方法的第一个参数是 cls
;@classmethod
def test_2(cls):
print('这是test_2方法,是一个类方法~~~ ',cls)
print(cls.count)
@staticmethod
来修饰的方法属于静态方法 ;@staticmethod
def test_3():
print('test_3执行了~~~')
A.test_3()
a.test_3()
完整代码:
class A(object):
# 类属性
# 实例属性
# 类方法
# 实例方法
# 静态方法
# 类属性,直接在类中定义的属性是类属性
# 类属性可以通过类或类的实例访问到
# 但是类属性只能通过类对象来修改,无法通过实例对象修改
count = 0
def __init__(self):
# 实例属性,通过实例对象添加的属性属于实例属性
# 实例属性只能通过实例对象来访问和修改,类对象无法访问修改
self.name = '孙悟空'
# 实例方法
# 在类中定义,以self为第一个参数的方法都是实例方法
# 实例方法在调用时,Python会将调用对象作为self传入
# 实例方法可以通过实例和类去调用
# 当通过实例调用时,会自动将当前调用对象作为self传入
# 当通过类调用时,不会自动传递self,此时我们必须手动传递self
def test(self):
print('这是test方法~~~ ' , self)
# 类方法
# 在类内部使用 @classmethod 来修饰的方法属于类方法
# 类方法的第一个参数是cls,也会被自动传递,cls就是当前的类对象
# 类方法和实例方法的区别,实例方法的第一个参数是self,而类方法的第一个参数是cls
# 类方法可以通过类去调用,也可以通过实例调用,没有区别
@classmethod
def test_2(cls):
print('这是test_2方法,他是一个类方法~~~ ',cls)
print(cls.count)
# 静态方法
# 在类中使用 @staticmethod 来修饰的方法属于静态方法
# 静态方法不需要指定任何的默认参数,静态方法可以通过类和实例去调用
# 静态方法,基本上是一个和当前类无关的方法,它只是一个保存到当前类中的函数
# 静态方法一般都是一些工具方法,和当前类无关
@staticmethod
def test_3():
print('test_3执行了~~~')
a = A()
# 实例属性,通过实例对象添加的属性属于实例属性
# a.count = 10
# A.count = 100
# print('A ,',A.count)
# print('a ,',a.count)
# print('A ,',A.name)
# print('a ,',a.name)
# a.test() 等价于 A.test(a)
# A.test_2() 等价于 a.test_2()
A.test_3()
a.test_3()
了解即可,实际应用中很少使用。
class A:
def __init__(self):
self.name = 'A类'
# del是一个特殊方法,它会在对象被垃圾回收前调用
def __del__(self):
print('A()对象被删除了~~~',self)
a = A()
a = None # 将a设置为了None,此时没有任何的变量对A()对象进行引用,它就是变成了垃圾
# 返回:A()对象被删除了~~~ <__main__.A object at 0x000001BB04D03A48>
__str__(self)
这个特殊方法会在尝试将对象转换为字符串的时候调用,它的作用可以用来指定对象转换为字符串的结果(print函数);def __str__(self):
return 'Person [name=%s , age=%d]'%(self.name,self.age)
__repr__(self)
这个特殊方法会在对当前对象使用repr()函数时调用def __repr__(self):
return 'Hello'
__bool__(self)
可以通过bool来指定对象转换为布尔值的情况def __bool__(self):
return self.age > 17
__gt__(self)
会在对象做大于比较的时候调用,该方法的返回值将会作为比较的结果;需要两个参数,一个self表示当前对象,other表示和当前对象比较的对象。# self > other
def __gt__(self , other):
return self.age > other.age
# 创建两个Person类的实例
p1 = Person('孙悟空',18)
p2 = Person('猪八戒',28)
# 打印p1
# 当我们打印一个对象时,实际上打印的是对象的中特殊方法 __str__()的返回值
# print(p1) # <__main__.Person object at 0x04E95090>
object.__add__(self, other)
object.__sub__(self, other)
object.__mul__(self, other)
object.__matmul__(self, other)
object.__truediv__(self, other)
object.__floordiv__(self, other)
object.__mod__(self, other)
object.__divmod__(self, other)
object.__pow__(self, other[, modulo])
object.__lshift__(self, other)
object.__rshift__(self, other)
object.__and__(self, other)
object.__xor__(self, other)
object.__or__(self, other)
object.__lt__(self, other) 小于 <
object.__le__(self, other) 小于等于 <=
object.__eq__(self, other) 等于 ==
object.__ne__(self, other) 不等于 !=
object.__gt__(self, other) 大于 >
object.__ge__(self, other) 大于等于 >=
参考:https://edu.aliyun.com/course/1782