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

继承和__slots__属性

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

  1. class SubClassName(ParentClass1 [, ParentClass2, ...]):
  2. class_suite

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

  1. class Parent(object):
  2. ''' parent class '''
  3. numList = []
  4. def numAdd(self, a, b):
  5. return a+b

  6. class Child(Parent):
  7. pass
  8.  
  9. c = Child()
  10. # subclass will inherit attributes from parent class
  11. Child.numList.extend(range(10))
  12. print Child.numList
  13. print "2 + 5 =", c.numAdd(2, 5)
  14.  
  15. # built-in function issubclass()
  16. print issubclass(Child, Parent)
  17. print issubclass(Child, object)
  18. # __bases__ can show all the parent classes
  19. print Child.__bases__
  20. # doc string will not be inherited
  21. print Parent.__doc__
  22. print Child.__doc__

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

   1)继承中的__init__

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

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

  1. class Parent(object):
  2. def __init__(self, data):
  3. self.data = data
  4. print "create an instance of:", self.__class__.__name__
  5. print "data attribute is:", self.data
  6.  
  7. class Child(Parent):
  8. pass
  9.  
  10. c = Child("init Child")
  11. print
  12. d = Child()

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

  1. class Parent(object):
  2. def __init__(self, data):
  3. self.data = data
  4. print "create an instance of:", self.__class__.__name__
  5. print "data attribute is:", self.data
  6.  
  7. class Child(Parent):
  8. def __init__(self):
  9. print "call __init__ from Child class"
  10.  
  11. c = Child()
  12. print c.data #出错

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

  1. class Parent(object):
  2. def __init__(self, data):
  3. self.data = data
  4. print "create an instance of:", self.__class__.__name__
  5. print "data attribute is:", self.data class
  6.  
  7. Child(Parent):
  8. def __init__(self):
  9. print "call __init__ from Child class"
  10. super(Child, self).__init__("data from Child")
  11.  
  12. c = Child()
  13. print c.data

2)super
    在子类中,一般会定义与父类相同的属性(数据属性,方法),从而来实现子类特有的行为。也就是说,子类会继承父类的所有的属性和方法,子类也可以覆盖父类同名的属性和方法。

  1. class Parent(object):
  2. fooValue = "Hi, Parent foo value"
  3. def foo(self):
  4. print "This is foo from Parent"
  5.  
  6. class Child(Parent):
  7. fooValue = "Hi, Child foo value"
  8. def foo(self):
  9. print "This is foo from Child"
  10.  
  11. c = Child()
  12. c.foo()
  13. print Child.fooValue

在这段代码中,子类的属性”fooValue”和”foo”覆盖了父类的属性,所以子类有了自己的行为。

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

  1. class Parent(object):
  2. fooValue = "Hi, Parent foo value"
  3. def foo(self):
  4. print "This is foo from Parent"
  5.  
  6. class Child(Parent):
  7. fooValue = "Hi, Child foo value"
  8. def foo(self):
  9. print "This is foo from Child"
  10. print Parent.fooValue
  11. # use Parent class name and self as an argument
  12. Parent.foo(self)
  13.  
  14. c = Child()
  15. c.foo()

这时候,可以通过父类名直接访问父类的属性,当调用父类的方法是,需要将”self”显示的传递进去的方式。

这种方式有一个不好的地方就是,需要经父类名硬编码到子类中,为了解决这个问题,可以使用Python中的super关键字:

  1. class Parent(object):
  2. fooValue = "Hi, Parent foo value"
  3. def foo(self):
  4. print "This is foo from Parent"
  5.  
  6. class Child(Parent):
  7. fooValue = "Hi, Child foo value"
  8. def foo(self):
  9. print "This is foo from Child"
  10. # use super to access Parent attribute
  11. print super(Child, self).fooValue
  12. super(Child, self).foo()
  13.  
  14. c = Child()
  15. c.foo()

对于”super(Child, self).foo()”可以理解为,首先找到Child的父类Parent,然后调用父类的foo方法,同时将Child的实例self传递给foo方法。

但是,如果当一个子类有多个父类的时候,super会如何工作呢?这是就需要看看MRO的概念了。

3)MRO

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

  1. class A(object):
  2. def __init__(self):
  3. print " ->Enter A"
  4. print " <-Leave A"
  5.  
  6. class B(A):
  7. def __init__(self):
  8. print " -->Enter B"
  9. A.__init__(self)
  10. print " <--Leave B"
  11.  
  12. class C(A):
  13. def __init__(self):
  14. print " --->Enter C"
  15. A.__init__(self)
  16. print " <---Leave C"
  17.  
  18. class D(B, C):
  19. def __init__(self):
  20. print "---->Enter D"
  21. B.__init__(self)
  22. C.__init__(self)
  23. print "<----Leave D"
    d = D()

从输出中可以看到,类A的初始化函数被调用了两次,这不是我们想要的结果:

下面,我们通过super方式来调用父类的初始化函数:

  1. class A(object):
  2. def __init__(self):
  3. print " ->Enter A"
  4. print " <-Leave A"
  5.  
  6. class B(A):
  7. def __init__(self):
  8. print " -->Enter B"
  9. super(B, self).__init__(self)
  10. print " <--Leave B"
  11.  
  12. class C(A):
  13. def __init__(self):
  14. print " --->Enter C"
  15. super(C, self).__init__(self)
  16. print " <---Leave C"
  17.  
  18. class D(B, C):
  19. def __init__(self):
  20. print "---->Enter D"
  21. super(D, self).__init__(self
      print "<----Leave D"
  22.  
  23. d = D()

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

为什么super会有这种效果?下面就开始看看Python中的方法解析顺序MRO(Method Resolution Order)。

Python的类有一个__mro__属性,这个属性中就保存着方法解析顺序。结合上面的例子来看看类D的__mro__:

  1. >>> print "MRO:", [x.__name__ for x in D.__mro__]MRO:
  2. ['D', 'B', 'C', 'A', 'object']

看到这里,对于上面使用super例子的输出就应该比较清楚了。

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

2. __slots__

从前面的介绍可以看到,当我们通过一个类创建了实例之后,仍然可以给实例添加属性,但是这些属性只属于这个实例。

有些时候,我们可以需要限制类实例对象的属性,这时就要用到类中的__slots__属性了。__slots__属性对于一个tuple,只有这个tuple中出现的属性可以被类实例使用。

  1. class Student(object):
  2. __slots__ = ("name", "age")
  3. def __init__(self, name, age):
  4. self.name = name
  5. self.age = age
  6.  
  7. s = Student("Wilber", 28)
  8. print "%s is %d years old" %(s.name, s.age)
  9. s.score = 96

在这个例子中,当场是给Student的实例s添加一个score属性的时候,就会遇到异常.

1) 子类没有__slots__属性

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

  1. class Person(object):
  2. __slots__ = ("name", "age")
  3. pass
  4. class Student(Person):
  5. pass
  6.  
  7. s = Student()
  8. s.name, s.age = "Wilber", 28
  9. s.score = 100
  10. print "%s is %d years old, score is %d" %(s.name, s.age, s.score)

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

2) 子类拥有__slots__属性

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

  1. class Person(object):
  2. __slots__ = ("name", "age")
  3. pass
  4. class Student(Person):
  5. __slots__ = ("score", )
  6. pass s = Student()
  7.  
  8. s.name, s.age = "Wilber", 28
  9. s.score = 100
  10. print "%s is %d years old, score is %d" %(s.name, s.age, s.score)
  11. print s.__slots__ s.city = "Shanghai"

代码的输出为:

所以说,对于__slots__属性:

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

总结

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

Python学习总结19:类(二)的更多相关文章

  1. Python学习 Part7:类

    Python学习 Part7:类 1. 作用域和命名空间 命名空间(namespace)就是一个从名称到对象的映射. 命名空间的一些实例:内置名称集(函数,像abs(),和内置异常名称),一个模块中的 ...

  2. python学习笔记4_类和更抽象

    python学习笔记4_类和更抽象 一.对象 class 对象主要有三个特性,继承.封装.多态.python的核心. 1.多态.封装.继承 多态,就算不知道变量所引用的类型,还是可以操作对象,根据类型 ...

  3. 《Python学习手册》(二)

    <Python学习手册>(二) --类型和运算 数字 十六进制 八进制 二进制 0x 0o 0b hex() oct() bin() >>>int('10',2) 2 & ...

  4. 【Python学习之七】类和对象

    环境 虚拟机:VMware 10 Linux版本:CentOS-6.5-x86_64 客户端:Xshell4 FTP:Xftp4 python3.6 一.面向对象编程1.概念(1)面向对象编程(OOP ...

  5. Python学习笔记 for windows 二

    函数 abs(-20)                                        //结果为:20,绝对值函数 def 函数名称([参数1,参数2,参数3]): 执行语句 retu ...

  6. Python学习笔记 - day7 - 类

    类 面向对象最重要的概念就是类(Class)和实例(Instance),比如球类,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同.在Python中,定义类 ...

  7. Python学习总结19:类(一)

    在Python中,可以通过class关键字定义自己的类,通过类私有方法“__init__”进行初始化.可以通过自定义的类对象类创建实例对象. class Student(object): count ...

  8. Python学习【第十二篇】模块(2)

    序列化 1.什么是python序列化? 把变量从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling 序列化就是将python的数据类型转换成字符串 反序列化就是将字符串转换成 ...

  9. python学习笔记系列----(二)控制流

    实际开始看这一章节的时候,觉得都不想看了,因为每种语言都会有控制流,感觉好像我不看就会了似的.快速预览的时候,发现了原来还包含了对函数定义的一些描述,重点讲了3种函数形参的定义方法,章节的最后讲述了P ...

随机推荐

  1. JAVA中的throws和throw的区别

    Java     一直对java中的throws和throw不太理解.最近一直在查这两个方面的资料,算是能明白一点吧.如果我下面的观点哪有不对,希望指出来,我加以改进.         throw:( ...

  2. Java 判断图片资源的存在否

    question: 如题,举个例子吧 String image ="http://info-database.csdn.net/Upload/2010-10-30/735-60sap1030 ...

  3. (IOS)Swift Music 程序分析

    本文主要分享下楼主在学习Swift编程过程中,对GitHub上的一个开源App Swift Music的研究心得. 项目地址:https://github.com/xujiyao123/SwiftMu ...

  4. Freemarker的第二次使用~list的元素是数组

    在上次初次使用Freemarker作为模版后,我再次使用它.这次的需求是: xml文档的某个节点的属性A和其一个子节点的某个属性B有关联,属性B的值需要随着属性A的值变化.而属性A的值有4个值,现在需 ...

  5. nrf51822-配对绑定实现过程

    关于配对绑定的一些原理内容这里不再重复介绍,看之前的几篇文档,静态密码,动态密码,连接时触发配对就可以了. 配对绑定的内容可能比较难懂,升入的学习需要去看规范,将前面的几篇相关文档看一遍实验一边再去看 ...

  6. job_chain

    JOB链:JOB之间的相互触发操作. 实验意图:有些JOB具有先后调用次序,比如先做一件事情,这件事情做完才能继续下一件事情,而第一个JOB如果自己挂掉的话,第二个JOB需要正常运行(默认是终止),这 ...

  7. ecshop transport.js 和 jquery 冲突解决办法

    您提供一个简单的解决transport.js 和 jquery 方法: 在 page_header.lbi 库文件中加入如下代码,注意操作顺序: 1.先导入transport.js  文件 {inse ...

  8. SqlServer2008R2执行Sql语句,快捷键

    SqlServer2008R2执行Sql语句,快捷键Alt+X

  9. C#对象的声明与实例化

    在代码中我们经常能看到下面这样的语句. A a = new A();(1) B b = null;(2) C c;(3) 代码( 1 ) 创建 A 的对象并对其进行初始化. A :类: new A() ...

  10. tomecat 配置修改 及启动配置

    a.如果tomcat是以bat方式启动的,则如下设置: 修改TOMCAT_HOME/bin/catalina.sh 在“echo "Using CATALINA_BASE: $CATALIN ...