深入理解python中的类和对象

您所在的位置:网站首页 python引用和对象的理解 深入理解python中的类和对象

深入理解python中的类和对象

2023-04-19 19:27| 来源: 网络整理| 查看: 265

刚开始学习python的时候或者其他面向对象的编程语言的时候,难免会对类和对象理解得不太清楚。所以今天和大家分享下python中的类和对象,深入理解下python中的类和对象。

1.鸭子类型

当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。这个就是鸭子类型的定义,在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

2.类变量和实例变量

类变量就是在类内定义的,但是不在方法内定义的,而且前缀无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,这个很容易知道,用类来引用改变时

Student.conutry = 'cn' # 这个是用类引用来进行修改

修改后打印下三个结果都是修改后的结果。但是下面这个呢?

s1.conutry = 'zhongguo' # 用实例来引用进行修改

这次结果就不一样了,只有s1的类变量变了,其他两个都是不变的。这是为什么呢?就如上面所说,用实例引用来修改类变量的时候并不是修改,而是新建了这个变量。又由于python查找变量是由下往上查找的,所以会先查找出新建后的变量。

3.类属性和实例属性之间的访问顺序

类属性就是定义在类中的方法和变量,实例属性也是一样的。访问顺序就是由下往上查找的,用代码体会一下。

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找。这种查找情况是没有问题的,但是另一种情况就不合适了。

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__) # 结果 (, , , , , )

__mro__这个属性是获取属性的查找顺序,可以看到就是和我们上面说的一样,用了深度优先的算法。再看另一个

class D(): pass class C(D): pass class B(D): pass class A(B, C): pass print(A.__mro__) # 结果 (, , , , )

这个时候就用了广度优先算法,符合与我们刚才所说的,这就是C3算法了哈。

4.super真的是调用父类吗?

学过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 (, , , , , )

也是和预期一样的。总的来说,super不一定是调用父类,它的调用顺序也是遵循mro算法的,就是属性查找算法,和上文说的C3算法一致。

有任何问题欢迎在留言区提问,或者有不当的地方也欢迎指出

ps:如果觉得文章不错的话,欢迎随手点赞转发支持



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3