上一篇介绍了Python中类相关的一些基本点,本文看看Python中类的继承和__slots__属性。

继承

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

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

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

  1. class Parent(object):
  2. '''
  3. parent class
  4. '''
  5. numList = []
  6. def numAdd(self, a, b):
  7. return a+b
  8.  
  9. class Child(Parent):
  10. pass
  11.  
  12. c = Child()
  13. # subclass will inherit attributes from parent class
  14. Child.numList.extend(range(10))
  15. print Child.numList
  16. print "2 + 5 =", c.numAdd(2, 5)
  17.  
  18. # built-in function issubclass()
  19. print issubclass(Child, Parent)
  20. print issubclass(Child, object)
  21.  
  22. # __bases__ can show all the parent classes
  23. print Child.__bases__
  24.  
  25. # doc string will not be inherited
  26. print Parent.__doc__
  27. print Child.__doc__

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

继承中的__init__

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

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

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

代码的输出为:

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

  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

代码的输出为:

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

  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. super(Child, self).__init__("data from Child")
  11.  
  12. c = Child()
  13. print c.data

代码的输出为:

super

前面一个例子中,已经看到了通过super来调用父类__init__方法的例子,下面看看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的概念了。

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"
  24.  
  25. 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__()
  10. print " <--Leave B"
  11.  
  12. class C(A):
  13. def __init__(self):
  14. print " --->Enter C"
  15. super(C, self).__init__()
  16. print " <---Leave C"
  17.  
  18. class D(B, C):
  19. def __init__(self):
  20. print "---->Enter D"
  21. super(D, self).__init__()
  22. print "<----Leave D"
  23.  
  24. d = D()

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

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

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

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

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

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

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

子类没有__slots__属性

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

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

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

子类拥有__slots__属性

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

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

代码的输出为:

所以说,对于__slots__属性:

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

总结

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

另外介绍了Python类的__slots__属性,通过这个属性可以限制类实例的可用属性。

Python中的类(中)的更多相关文章

  1. python---Django中模型类中Meta元对象了解

    Django中模型类中Meta元对象了解 1.使用python manage.py shell 进入编辑命令行模式,可以直接进入项目(为我们配置好了环境) python manage.py shell ...

  2. c++中在一个类中定义另一个只有带参数构造函数的类的对象

    c++中在一个类中定义另一个只有带参数构造函数的类的对象,编译通不过 #include<iostream> using namespace std; class A { public:  ...

  3. Java中主类中定义方法加static和不加static的区别

     Java中主类中定义方法加static和不加static的区别(前者可以省略类名直接在主方法调用(类名.方法),后者必须先实例化后用实例调用) 知识点:1.Getter and Setter 的应用 ...

  4. C++中若类中没有默认构造函数,如何使用对象数组

    前言: 如果定义一个类,有其默认的构造函数,则使用new动态实例化一个对象数组,不是件难事,如下代码: #include <memory> #include <iostream> ...

  5. python第三十六天-----类中的特殊成员方法

    __doc__ 查看尖的描述信息 __module__表示当前操作的对象所在的模块 __class__表示当前操作的对象所属的类 __init__构造方法 通过类创建对象自动执行 __del__析构方 ...

  6. python中的类中属性元素加self.和不加self.的区别

    在类中,self只能在函数中使用,表示的是实例属性,就是每个实例可以设置不值,而不相互影响.如果在类级别使用没有self的属性,是类属性,一般作为全局变量来用的.事实上:就是一个是类属性 一个是对象属 ...

  7. 转载:java 中对类中的属性使用set/get方法的意义和用法

    经常看到有朋友提到类似:对类中的属性使用set/get方法的作用?理论的回答当然是封闭性之类的,但是这样对我们有什么作用呢?为什么要这样设计?我直接使用属性名来访问不是更直接,代码更简洁明了吗?下面我 ...

  8. 【Java基础之Object类(一)】Java中Object类中的所有方法(toString、equals、hashCode、clone、finalize、wait和notify等)详解(转载)

    java中的hashcode.equals和toString方法都是基类Object的方法. 首先说说toString方法,简单的总结了下API说明就是:返回该对象的字符串表示,信息应该是简明但易于读 ...

  9. 4. 在Inspector面板中显示类中变量+ 拓展编辑器

    1. C#脚本如下: using UnityEngine; using System.Collections; public class MyTest : MonoBehaviour { ; ; [S ...

  10. java中File类中list()和listFiles()方法区别

    list()和listFiles()方法区别: 1.返回值类型不同:前者为String数组,后者为File对象数组 2.数组中元素内容不同:前者为string类型的[文件名](包含后缀名),后者为Fi ...

随机推荐

  1. 如何在ROS中使用PCL—数据格式(1)

    在ROS中点云的数据类型 在ROS中表示点云的数据结构有: sensor_msgs::PointCloud      sensor_msgs::PointCloud2     pcl::PointCl ...

  2. [hbase] 查询数据

    获取hbase数据 说说:count 'SOCIA:T_SOCIA_ALBUM_TALK'scan 'SOCIA:T_SOCIA_ALBUM_TALK',{LIMIT=>5}get 'SOCIA ...

  3. (笔记)Linux中的终端、控制台、tty、pty

    1>tty(终端设备的统称): tty一词源于teletypes,或者teletypewriters,原来指的是电传打字机,是通过串行线用打印机键盘阅读和发送信息的东西,后来这东西被键盘与显示器 ...

  4. Unity如何内置Visual Studio

    一.问题的起源 软件环境:Unity 2017.3.0f3,Visual Studio 2013 问题描述:在Unity中创建C#脚本后,准备双击打开进行编辑时,出现了Fatal Error. 二.问 ...

  5. 转载: PHP错误:Warning: Cannot modify header information - headers already sent by ...

    如果在执行php程序时看到这条警告:"Warning: Cannot modify header information - headers already sent by ....&quo ...

  6. Mybatis系列(五):mybatis逆向工程

    一.背景 在实际开发中我们会自己去写mapper映射文件,接口,数据库表对应的实体类,如果需求任务比较少,咱们还可以慢慢的一个一个去写,但是这是不现实的,因为在工作中我们的任务是很多的,这时mybat ...

  7. Spring JDBC JdbcTemplate类示例

    org.springframework.jdbc.core.JdbcTemplate类是JDBC核心包中的中心类.它简化了JDBC的使用,并有助于避免常见的错误. 它执行核心JDBC工作流,留下应用程 ...

  8. (原)在firefly_rk3288开发板上解决openGL在设置32位色深以后出现花屏的问题

    转载请注明出处:http://www.cnblogs.com/lihaiping/p/5567141.html 在做openGL测试的过程中,根据论坛上的帖子,在使用/bin/fbset -a -no ...

  9. 认识J2EE规范或标准以及J2EE和JEE有什么不同?

    1. J2EE实际上是一组规范(新手对规范这个词可能云里雾里的,没有办法,JAVA概念太多了,大部分概念慢慢就会理解),没错,J2EE这个概念并不是某种技术,而是一堆规范(实现意义上可以说是一堆技术) ...

  10. 推荐个office能在线预览的插件

    1.chrome  office viewer 这个可以离线使用 2.微软 office web app  可以使用微软在线服务器或则自己搭建服务器 有兴趣的朋友百度一下具体操作方法