Py学习  »  Python

深入理解python中的类和对象

sergiojune • 5 年前 • 435 次点击  
阅读 16

深入理解python中的类和对象

刚开始学习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(BC):
   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(BC):
   pass

print(A.__mro__)
# 结果
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C '>, <class '__main__.D'>, <class 'object'>)复制代码

这个时候就用了广度优先算法,符合与我们刚才所说的,这就是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(DC):
   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:如果觉得文章不错的话,欢迎随手点赞转发支持

日常学python

代码不止bug,还有美和乐趣


今天看啥 - 高品质阅读平台
本文地址:http://www.jintiankansha.me/t/KN8YCTCnEP
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/21815
 
435 次点击