设为首页 收藏本站
查看: 1307|回复: 0

[经验分享] Python中的类(中)

[复制链接]

尚未签到

发表于 2015-11-29 09:42:00 | 显示全部楼层 |阅读模式
  上一篇介绍了Python中类相关的一些基本点,本文看看Python中类的继承和__slots__属性。

继承
  在Python中,同时支持单继承与多继承,一般语法如下:



class SubClassName(ParentClass1 [, ParentClass2, ...]):
class_suite
  实现继承之后,子类将继承父类的属性,也可以使用内建函数insubclass()来判断一个类是不是另一个类的子孙类:



class Parent(object):
'''
parent class
'''
numList = []
def numAdd(self, a, b):
return a+b
class Child(Parent):
pass

c = Child()   
# subclass will inherit attributes from parent class   
Child.numList.extend(range(10))
print Child.numList
print "2 + 5 =", c.numAdd(2, 5)
# built-in function issubclass()
print issubclass(Child, Parent)
print issubclass(Child, object)
# __bases__ can show all the parent classes
print Child.__bases__
# doc string will not be inherited
print Parent.__doc__
print Child.__doc__
  代码的输出为,例子中唯一特别的地方是文档字符串。文档字符串对于类,函数/方法,以及模块来说是唯一的,也就是说__doc__属性是不能从父类中继承来的。
DSC0000.png

继承中的__init__
  当在Python中出现继承的情况时,一定要注意初始化函数__init__的行为。
  1. 如果子类没有定义自己的初始化函数,父类的初始化函数会被默认调用;但是如果要实例化子类的对象,则只能传入父类的初始化函数对应的参数,否则会出错。



class Parent(object):
def __init__(self, data):
self.data = data
print "create an instance of:", self.__class__.__name__
print "data attribute is:", self.data
class Child(Parent):
pass
c = Child("init Child")
print   
c = Child()   
  代码的输出为:
DSC0001.png
  2. 如果子类定义了自己的初始化函数,而没有显示调用父类的初始化函数,则父类的属性不会被初始化



class Parent(object):
def __init__(self, data):
self.data = data
print "create an instance of:", self.__class__.__name__
print "data attribute is:", self.data
class Child(Parent):
def __init__(self):
print "call __init__ from Child class"
c = Child()   
print c.data
  代码的输出为:
DSC0002.png
  3. 如果子类定义了自己的初始化函数,显示调用父类,子类和父类的属性都会被初始化



class Parent(object):
def __init__(self, data):
self.data = data
print "create an instance of:", self.__class__.__name__
print "data attribute is:", self.data
class Child(Parent):
def __init__(self):
print "call __init__ from Child class"
super(Child, self).__init__("data from Child")
c = Child()   
print c.data
  代码的输出为:
DSC0003.png

super
  前面一个例子中,已经看到了通过super来调用父类__init__方法的例子,下面看看super的使用。
  在子类中,一般会定义与父类相同的属性(数据属性,方法),从而来实现子类特有的行为。也就是说,子类会继承父类的所有的属性和方法,子类也可以覆盖父类同名的属性和方法。



class Parent(object):
fooValue = "Hi, Parent foo value"
def foo(self):
print "This is foo from Parent"
class Child(Parent):
fooValue = "Hi, Child foo value"
def foo(self):
print "This is foo from Child"
c = Child()   
c.foo()
print Child.fooValue
  在这段代码中,子类的属性"fooValue"和"foo"覆盖了父类的属性,所以子类有了自己的行为。
DSC0004.png
  但是,有时候可能需要在子类中访问父类的一些属性:



class Parent(object):
fooValue = "Hi, Parent foo value"
def foo(self):
print "This is foo from Parent"
class Child(Parent):
fooValue = "Hi, Child foo value"
def foo(self):
print "This is foo from Child"
print Parent.fooValue
# use Parent class name and self as an argument
        Parent.foo(self)
c = Child()   
c.foo()
  这时候,可以通过父类名直接访问父类的属性,当调用父类的方法是,需要将"self"显示的传递进去的方式。
DSC0005.png
  这种方式有一个不好的地方就是,需要经父类名硬编码到子类中,为了解决这个问题,可以使用Python中的super关键字:



class Parent(object):
fooValue = "Hi, Parent foo value"
def foo(self):
print "This is foo from Parent"
class Child(Parent):
fooValue = "Hi, Child foo value"
def foo(self):
print "This is foo from Child"
# use super to access Parent attribute
print super(Child, self).fooValue
super(Child, self).foo()
c = Child()   
c.foo()
  对于"super(Child, self).foo()"可以理解为,首先找到Child的父类Parent,然后调用父类的foo方法,同时将Child的实例self传递给foo方法。
  但是,如果当一个子类有多个父类的时候,super会如何工作呢?这是就需要看看MRO的概念了。

MRO
  假设现在有一个如下的继承结构,首先通过类名显示调用的方式来调用父类的初始化函数:



class A(object):
def __init__(self):
print "   ->Enter A"
print "   <-Leave A"
class B(A):
def __init__(self):
print "  -->Enter B"
A.__init__(self)
print "  <--Leave B"
class C(A):
def __init__(self):
print " --->Enter C"
A.__init__(self)
print " <---Leave C"
class D(B, C):
def __init__(self):
print "---->Enter D"
B.__init__(self)
C.__init__(self)
print "<----Leave D"
d = D()
  从输出中可以看到,类A的初始化函数被调用了两次,这不是我们想要的结果:
DSC0006.png
  下面,我们通过super方式来调用父类的初始化函数:



class A(object):
def __init__(self):
print "   ->Enter A"
print "   <-Leave A"
class B(A):
def __init__(self):
print "  -->Enter B"
super(B, self).__init__()
print "  <--Leave B"
class C(A):
def __init__(self):
print " --->Enter C"
super(C, self).__init__()
print " <---Leave C"
class D(B, C):
def __init__(self):
print "---->Enter D"
super(D, self).__init__()
print "<----Leave D"
d = D()
  通过输出可以看到,当使用super后,A的初始化函数只能调用了一次:
DSC0007.png
  为什么super会有这种效果?下面就开始看看Python中的方法解析顺序MRO(Method Resolution Order)。
  Python的类有一个__mro__属性,这个属性中就保存着方法解析顺序。结合上面的例子来看看类D的__mro__:



>>> print "MRO:", [x.__name__ for x in D.__mro__]
MRO: ['D', 'B', 'C', 'A', 'object']
>>>
  看到这里,对于上面使用super例子的输出就应该比较清楚了。


  • Python的多继承类是通过MRO的方式来保证各个父类的函数被逐一调用,而且保证每个父类函数只调用一次(如果每个类都使用super)
  • 混用super类和非绑定的函数是一个危险行为,这可能导致应该调用的父类函数没有调用或者一个父类函数被调用多次

__slots__
  从前面的介绍可以看到,当我们通过一个类创建了实例之后,仍然可以给实例添加属性,但是这些属性只属于这个实例。
  有些时候,我们可以需要限制类实例对象的属性,这时就要用到类中的__slots__属性了。__slots__属性对于一个tuple,只有这个tuple中出现的属性可以被类实例使用。



class Student(object):
__slots__ = ("name", "age")
def __init__(self, name, age):
self.name = name
self.age = age
s = Student("Wilber", 28)        
print "%s is %d years old" %(s.name, s.age)
s.score = 96
  在这个例子中,当场是给Student的实例s添加一个score属性的时候,就会遇到下面的异常:
DSC0008.png

子类没有__slots__属性
  使用__slots__要注意,__slots__定义的属性仅对当前类的实例起作用,对继承的子类实例是不起作用的:



class Person(object):
__slots__ = ("name", "age")
pass
class Student(Person):
pass
s = Student()
s.name, s.age = "Wilber", 28
s.score = 100
print "%s is %d years old, score is %d" %(s.name, s.age, s.score)
  从代码的输出可以看到,子类Student的实例并不受父类中__slots__属性的限制:
DSC0009.png

子类拥有__slots__属性
  但是,如果子类本身也有__slots__属性,子类的属性就是自身的__slots__加上父类的__slots__。



class Person(object):
__slots__ = ("name", "age")
pass
class Student(Person):
__slots__ = ("score", )
pass
s = Student()
s.name, s.age = "Wilber", 28
s.score = 100
print "%s is %d years old, score is %d" %(s.name, s.age, s.score)
print s.__slots__
s.city = "Shanghai"
  代码的输出为:
DSC00010.png
  所以说,对于__slots__属性:


  • 如果父类包含对__slots__的定义,子类不包含对__slots__的定义,解释器忽略__slots__的作用
  • 如果父类包含对__slots__的定义,子类包含对__slots__的定义,并且无论元组的的元素个数,解释器都会按照父类的__slots__和子类的__slots__的并集来检查

总结
  本文介绍了Python中的继承,当使用多继承的时候,可以使用super关键字去访问父类中被子类覆盖的方法;对于方法的调用,需要参照MRO。
  另外介绍了Python类的__slots__属性,通过这个属性可以限制类实例的可用属性。
  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-144807-1-1.html 上篇帖子: Python开发入门与实战1-开发环境 下篇帖子: [LeetCode]题解(python):004-Median of Two Sorted Arrays
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表