sergiojune 平常学python
刚开始学习python的时候或者其余的是面向对象的编程语言的时候,不免会对类和对象理解得不太清楚。因此今天和你们分享下python中的类和对象,深刻理解下python中的类和对象。python
当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就能够被称为鸭子。这个就是鸭子类型的定义,在python中,并不关心这个对象是什么类型,只关心他的行为。由行为来推断出该对象所属于的类型。就好比列表(list)、元组(tuple)、字典(dict)等等,这些类都是可迭代的,因此说他们是可迭代对象。算法
from collections import Iterable l = [1, ] t = (1, ) d = {'d': 3} print(isinstance(l, Iterable)) print(isinstance(t, Iterable)) print(isinstance(d, Iterable)) # 结果 True True True
类变量就是在类内定义的,可是不在方法内定义的,并且前缀无self做为引用。实例变量就是有self做为引用的存在类中的变量。类变量是全部对象共享的,在类中修改时,其余的对象也会跟着变。可是须要注意的是,若是是用对象来引用类变量进行修改的话,这里只是新建了和类变量同名的实例变量,并无修改到。下面用代码解释下。编程
class Student(object): conutry = 'China' # 这个是类变量 def __init__(self, name, sex): self.name = name # 这个是实例变量,也就是对象变量 self.sex = sex # 对象变量 s1 = Student('张三', 'man') s2 = Student('里斯', 'woman') print(s1.conutry) print(s2.conutry) print(Student.conutry)
上面的结果都是三个China,这个很容易知道,用类来引用改变时markdown
Student.conutry = 'cn' # 这个是用类引用来进行修改
修改后打印下三个结果都是修改后的结果。可是下面这个呢?编程语言
s1.conutry = 'zhongguo' # 用实例来引用进行修改
此次结果就不同了,只有s1的类变量变了,其余两个都是不变的。这是为何呢?就如上面所说,用实例引用来修改类变量的时候并非修改,而是新建了这个变量。又因为python查找变量是由下往上查找的,因此会先查找出新建后的变量。ide
类属性就是定义在类中的方法和变量,实例属性也是同样的。访问顺序就是由下往上查找的,用代码体会一下。函数
class A(): name = 'A' def __init__(self): self.name = 'a' a = A() print(a.name) # 结果 a
因为是类变量先加载,再到初始化对象,因此才会运行init()方法,因此结果很显然就是a。这里因为该类没有继承,就没有很复杂,可是当出现多继承,几个类之间就变得很复杂,这个时候的访问顺序就难多了。下面说下这两种状况,掌握了这两种状况,其余的基本没有问题了。学习
(1.适合深度优先查找
A继承了B,C,B,C分别继承了D,E。深度优先的查找是先去着A,若是A中没有该属性,就去B着,再没有就去D找。D中找不到了再去C找。这种查找状况是没有问题的,可是另外一种状况就不合适了。code
2)适合广度优先查找
这个是A继承了B,C,B,C都继承了D。若是这个用深度优先的算法的话,就会出现一个问题,由于深度优先查找顺序是A->B->D->C。这个是不太合理的,当C中重载了D中的一个方法后,B没有重载,若是要查找C中的该方法,用深度优先的算法就只能找到D中的原始方法,因此说这就不正确了,这时候就须要用广度优先的 算法,这个时候查找顺序就是A->B->C->D。可是当遇到上面的状况时又会出错了。这时怎么办?python3就将全部的属性搜索算法统一成了一个算法:C3算法,这里就不展开说这个算法了,由于太复杂了:)会根据对应状况而实施对应算法,下面用代码来分别体会下以上两种状况对象
class E(): pass class D(): pass class C(E): pass class B(D): pass class A(B, C): pass print(A.__mro__) # 结果 (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.C'>, <class '__main__.E'>, <class 'object'>)
mro这个属性是获取属性的查找顺序,能够看到就是和咱们上面说的同样,用了深度优先的算法。再看另外一个
class D(): pass class C(D): pass class B(D): pass class A(B, C): pass print(A.__mro__) # 结果 (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class 'object'>)
这个时候就用了广度优先算法,符合与咱们刚才所说的,这就是C3算法了哈。
学过Java的都知道,super()这个方法就是在调用父类的方法,可是在python中就不必定了。咱们先看看super的用法
class A(): def __init__(self): print('A') class B(A): def __init__(self): # python2作法是 # super(A, self).__init__() super().__init__() # 调用父类的初始化方法 print('B') b = B() # 结果 A B
上面就是用法了,python2和python3用法不同,这里咱们就只用python3了就行操做。接下来看看super真正的调用状况。
class A(): def __init__(self): print('A') class B(A): def __init__(self): super().__init__() print('B') class C(A): def __init__(self): super().__init__() print('C') class D(B, C): def __init__(self): super().__init__() print('D') d = D()
上面这个是咱们以前所说的那个适合广度优先算法的多继承,按照咱们以前的理解,super调用的是父函数,那么这个结果就会是:
A B C D
显然是错误,结果是这个
是否是以为很奇怪,可是又很熟悉?是的,这个也是按照刚才的查找顺序同样执行的,若是不信的话咱们打印下mro就知道了
是否是恰好倒叙?由于咱们是先打印父类的再打印本身的,因此顺序倒了。再看看另一种状况也是可行的
class A(): def __init__(self): print('A') class B(): def __init__(self): super().__init__() print('B') class C(A): def __init__(self): super().__init__() print('C') class D(B): def __init__(self): super().__init__() print('D') class E(D, C): def __init__(self): super().__init__() print('E') e = E() print(E.__mro__) # 结果 A C B D E (<class '__main__.E'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
也是和预期同样的。总的来讲,super不必定是调用父类,它的调用顺序也是遵循mro算法的,就是属性查找算法,和上文说的C3算法一致。
有任何问题欢迎在留言区提问,或者有不当的地方也欢迎指出
ps:若是以为文章不错的话,欢迎随手点赞转发支持