python 继承与多重继承

固然,若是不支持python继承,语言特性就不值得称为“类”。派生类定义的语法以下所示:html

<statement-1> . . . <statement-N>

名称 BaseClassName 必须定义于包含派生类定义的做用域中。 也容许用其余任意表达式代替基类名称所在的位置。 这有时也可能会用得上,例如,当基类定义在另外一个模块中的时候:python

class DerivedClassName(modname.BaseClassName):
派生类定义的执行过程与基类相同。 当构造类对象时,基类会被记住。 此信息将被用来解析属性引用:若是请求的属性在类中找不到,搜索将转往基类中进行查找。 若是基类自己也派生自其余某个类,则此规则将被递归地应用。nginx

派生类的实例化没有任何特殊之处: DerivedClassName() 会建立该类的一个新实例。 方法引用将按如下方式解析:搜索相应的类属性,若有必要将按基类继承链逐步向下查找,若是产生了一个函数对象则方法引用就生效。程序员

派生类可能会重载其基类的方法。 由于方法在调用同一对象的其余方法时没有特殊权限,调用同一基类中定义的另外一方法的基类方法最终可能会调用覆盖它的派生类的方法。 (对 C++ 程序员的提示:Python 中全部的方法实际上都是 virtual 方法。)算法

在派生类中的重载方法实际上可能想要扩展而非简单地替换同名的基类方法。 有一种方式能够简单地直接调用基类方法:即调用 BaseClassName.methodname(self, arguments)。 有时这对客户端来讲也是有用的。 (请注意仅当此基类可在全局做用域中以 BaseClassName 的名称被访问时方可以使用此方式。)缓存

Python有两个内置函数可被用于继承机制:app

使用 isinstance() 来检查一个实例的类型: isinstance(obj, int) 仅会在 obj.__class__ 为 int 或某个派生自 int 的类时为 True。
使用 issubclass() 来检查类的继承关系: issubclass(bool, int) 为 True,由于 bool 是 int 的子类。 可是,issubclass(float, int) 为 False,由于 float 不是 int 的子类。ide

多重继承函数

class DerivedClassName(Base1, Base2, Base3): <statement-1> . . . <statement-N>

对于多数应用来讲,在最简单的状况下,你能够认为搜索从父类所继承属性的操做是深度优先、从左至右的,当层次结构中存在重叠时不会在同一个类中搜索两次。 所以,若是某一属性在 DerivedClassName 中未找到,则会到 Base1 中搜索它,而后(递归地)到 Base1 的基类中搜索,若是在那里未找到,再到 Base2 中搜索,依此类推。spa

真实状况比这个更复杂一些;方法解析顺序会动态改变以支持对 super() 的协同调用。 这种方式在某些其余多重继承型语言中被称为后续方法调用,它比单继承型语言中的 super 调用更强大。

动态改变顺序是有必要的,由于全部多重继承的状况都会显示出一个或更多的菱形关联(即至少有一个父类可经过多条路径被最底层类所访问)。 例如,全部类都是继承自 object,所以任何多重继承的状况都提供了一条以上的路径能够通向 object。 为了确保基类不会被访问一次以上,动态算法会用一种特殊方式将搜索顺序线性化, 保留每一个类所指定的从左至右的顺序,只调用每一个父类一次,而且保持单调(即一个类能够被子类化而不影响其父类的优先顺序)。 总而言之,这些特性使得设计具备多重继承的可靠且可扩展的类成为可能。

私有变量

那种仅限从一个对象内部访问的“私有”实例变量在 Python 中并不存在。 可是,大多数 Python 代码都遵循这样一个约定:带有一个下划线的名称 (例如 _spam) 应该被看成是 API 的非仅供部分 (不管它是函数、方法或是数据成员)。 这应当被视为一个实现细节,可能不经通知即加以改变。

因为存在对于类私有成员的有效使用场景(例如避免名称与子类所定义的名称相冲突),所以存在对此种机制的有限支持,称为 名称改写。 任何形式为 __spam 的标识符(至少带有两个前缀下划线,至多一个后缀下划线)的文本将被替换为 _classname__spam,其中 classname 为去除了前缀下划线的当前类名称。 这种改写不考虑标识符的句法位置,只要它出如今类定义内部就会进行。

名称改写有助于让子类重载方法而不破坏类内方法调用。例如:

def __init__(self, iterable): self.items_list = [] self.__update(iterable) def update(self, iterable): for item in iterable: self.items_list.append(item) __update = update # private copy of original update() method class MappingSubclass(Mapping): def update(self, keys, values): # provides new signature for update() # but does not break __init__() for item in zip(keys, values): self.items_list.append(item)

上面的示例即便在 MappingSubclass 引入了一个 __update 标识符的状况下也不会出错,由于它会在 Mapping 类中被替换为 _Mapping__update 而在 MappingSubclass 类中被替换为 _MappingSubclass__update。

请注意,改写规则的设计主要是为了不意外冲突;访问或修改被视为私有的变量仍然是可能的。这在特殊状况下甚至会颇有用,例如在调试器中。

请注意传递给 exec() 或 eval() 的代码不会将发起调用类的类名视做当前类;这相似于 global 语句的效果,所以这种效果仅限于同时通过字节码编译的代码。 一样的限制也适用于 getattr(), setattr() 和 delattr(),以及对于 dict 的直接引用。

杂项说明

有时会须要使用相似于 Pascal 的“record”或 C 的“struct”这样的数据类型,将一些命名数据项捆绑在一块儿。 这种状况适合定义一个空类:

pass john = Employee() # Create an empty employee record # Fill the fields of the record john.name = 'John Doe' john.dept = 'computer lab' john.salary = 1000

一段须要特定抽象数据类型的 Python 代码每每能够被传入一个模拟了该数据类型的方法的类做为替代。 例如,若是你有一个基于文件对象来格式化某些数据的函数,你能够定义一个带有 read() 和 readline() 方法从字符串缓存获取数据的类,并将其做为参数传入。

实例方法对象也具备属性: m.__self__ 就是带有 m() 方法的实例对象,而 m.__func__ 则是该方法所对应的函数对象。

相关文章
相关标签/搜索