博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python学习总结19:类(二)
阅读量:7211 次
发布时间:2019-06-29

本文共 6443 字,大约阅读时间需要 21 分钟。

参考:

继承和__slots__属性

1. 继承

    在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.numListprint "2 + 5 =", c.numAdd(2, 5) # built-in function issubclass() print issubclass(Child, Parent)print issubclass(Child, object) # __bases__ can show all the parent classesprint Child.__bases__ # doc string will not be inheritedprint Parent.__doc__print Child.__doc__

   代码的输出为,例子中唯一特别的地方是文档字符串。文档字符串对于类,函数/方法,以及模块来说是唯一的,也就是说__doc__属性是不能从父类中继承来的。

   1)继承中的__init__

      当在Python中出现继承的情况时,一定要注意初始化函数__init__的行为。

      如果子类没有定义自己的初始化函数,父类的初始化函数会被默认调用;但是如果要实例化子类的对象,则只能传入父类的初始化函数对应的参数,否则会出错。

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    d = Child()

    d 实例化的时候出错。

    如果子类定义了自己的初始化函数,而没有显示调用父类的初始化函数,则父类的属性不会被初始化。

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 #出错

    如果子类定义了自己的初始化函数,显示调用父类,子类和父类的属性都会被初始化

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

2)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”覆盖了父类的属性,所以子类有了自己的行为。

    但是,有时候可能需要在子类中访问父类的一些属性:

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”显示的传递进去的方式。

    这种方式有一个不好的地方就是,需要经父类名硬编码到子类中,为了解决这个问题,可以使用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的概念了。

3)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的初始化函数被调用了两次,这不是我们想要的结果:

    下面,我们通过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__(self)        print "  <--Leave B" class C(A):    def __init__(self):        print " --->Enter C"        super(C, self).__init__(self)        print " <---Leave C" class D(B, C):    def __init__(self):        print "---->Enter D"        super(D, self).__init__(self         print "<----Leave D" d = D()

    通过输出可以看到,当使用super后,A的初始化函数只能调用了一次。

     为什么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类和非绑定的函数是一个危险行为,这可能导致应该调用的父类函数没有调用或者一个父类函数被调用多次

2. __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属性的时候,就会遇到异常.

     1) 子类没有__slots__属性

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

class Person(object):    __slots__ = ("name", "age")    pass class Student(Person):    pass s = Student()s.name, s.age = "Wilber", 28s.score = 100 print "%s is %d years old, score is %d" %(s.name, s.age, s.score)

     从代码的输出可以看到,子类Student的实例并不受父类中__slots__属性的限制:

     2) 子类拥有__slots__属性

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

class Person(object):    __slots__ = ("name", "age")    pass class Student(Person):    __slots__ = ("score", )    pass s = Student()s.name, s.age = "Wilber", 28s.score = 100print "%s is %d years old, score is %d" %(s.name, s.age, s.score)print s.__slots__ s.city = "Shanghai"

    代码的输出为:

    所以说,对于__slots__属性:

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

总结

    本文介绍了Python中的继承,当使用多继承的时候,可以使用super关键字去访问父类中被子类覆盖的方法;对于方法的调用,需要参照MRO。

 

转载于:https://www.cnblogs.com/zhuxiaohou110908/p/5783828.html

你可能感兴趣的文章
勒索病毒入侵中国, Splunk建议网络立即进行区分和隔离设置
查看>>
应“云”而生--云时代的运维新理念
查看>>
RFID能否让实体零售业度过“寒冬”?
查看>>
Swagger2接口注释参数使用数组
查看>>
“IP的力量”专题论坛成功举办,聚焦行业共话IP与VR AR技术新融合
查看>>
Ubuntu下使用UFW配置防火墙(简化iptables的操作)
查看>>
OpenStack快速入门-queens版本
查看>>
大数据驱动智能制造 物联网引爆工业革命商机
查看>>
一个比较完善的购物车类
查看>>
「镁客·请讲」Visense Studio冯樑杰:游戏基因的VR视频,最好的表现是真人实拍交互...
查看>>
让人欲罢不能的量子学
查看>>
美团在Redis上踩过的一些坑-2.bgrewriteaof问题
查看>>
C# StreamReader.ReadLine统计行数的问题
查看>>
异常测试实践与梳理
查看>>
多者异也
查看>>
tf:'hello tensorflow'
查看>>
RedisConf2018记录--Day 1 sessions
查看>>
CentOS的el5, el6, el7代表什么
查看>>
柏林纪行(中):Node.js Collaboration Summit
查看>>
IT网络通信大变革时代来临 2016中国极客大奖为您找到风向标
查看>>