zoukankan      html  css  js  c++  java
  • Python学习总结19:类(二)

    参考:http://python.jobbole.com/82308/

    继承和__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.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__属性是不能从父类中继承来的。

       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", 28
    s.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", 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" 

        代码的输出为:

        所以说,对于__slots__属性:

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

    总结

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

  • 相关阅读:
    Vue2+VueRouter2+webpack 构建项目实战(一):准备工作
    vue+webpack 安装常见插件
    JS与CSS阻止元素被选中及清除选中的方法总结
    IE浏览器的ActiveXObject对象以及FileSystemobject的应用扩展(完成)
    用webpack2.0构建vue2.0超详细精简版
    从淘宝和网易的font-size思考移动端怎样使用rem?
    用CSS开启硬件加速来提高网站性能
    JS实现数组去重方法整理
    [总结]高效的jQuery代码编写技巧
    JS apply的巧妙用法以及扩展到Object.defineProperty的使用
  • 原文地址:https://www.cnblogs.com/zhuxiaohou110908/p/5783828.html
Copyright © 2011-2022 走看看