关于类的基础内容,包括类的定义,类对象,实例对象,方法对象,类和实例变量。在下面的这篇文章已经有基本的介绍:python
https://blog.csdn.net/weixin_45642918/article/details/104579523微信
今天这篇文章主要介绍类的继承以及私有变量。app
Python 支持类的继承。下面是派生类的定义:函数
class DerivedClassName(BaseClassName): <statement-1> . . . <statement-N>
在这里基类 BaseClassName 与派生类必须定义在同一个做用域中。也容许其余任意表达式代替基类名称所在的位置。例如,基类定义在另外一个模块中时:spa
class DerivedClassName(modname.BaseClassName):
派生类跟基类执行过程是同样的。当构造类对象的时候,基类实际上是会被先记下。当解析属性引用的时候:若是请求属性在派生类中没有找到时,将会从基类中进行搜索。如果继承的基类也是派生自其余类,则递归应用此规则。.net
派生类的实例化也没有太特殊的地方:DerivedClassName() 建立类的新实例。方法引用解析以下:搜索相应的类属性,若是有必要的话,会按基类继承链逐步往下找,若是产生了一个函数对象则方法引用就生效。例如:设计
>>> class Animal: ... def run(self): ... print('Animal is running') ... >>> class Dog(Animal): ... pass ... >>> d = Dog() >>> d.run() Animal is running
在这里,Dog 继承 Animal 基类,Dog()
建立新的实例并赋值给 d
,当调用 run
方法的时候,Dog 类并无相应的方法,因此在基类 Animal 中搜索,Animal 中存在这个方法,产生函数对象,引用生效。返回的结果则是基类方法中的内容。code
派生类是能够重载基类的方法。由于当调用同个对象的其余方法时,基类方法是没有其余特殊权限的。因此基类的方法调用同一基类中定义的另外一个方法可能最终会调用覆盖它的派生类的方法。对象
在上面的例子中,Dog 并无相应的方法,如今也编写一个一样名为 run 的方法,再看调用结果:blog
>>> class Animal: ... def run(self): ... print('Animal is running') ... >>> class Dog(Animal): ... def run(self): ... print('Dog is running') ... >>> d = Dog() >>> d.run() Dog is running
在这里,Dog 类的 run()
方法重载基类 Animal 的 run()
方法,当调用 run()
方法的时候,会首先调用子类重载的方法。
但在派生类中的重载方法实际上可能想要扩展而并不是简单地替换同名的基类方法。可是有一种方式能够简单地调用基类方法:BaseClassName.methodname(self, arguments)
。(这里须要注意,这种方式仅在基类 BaseClassName
可在全局做用域以此名称被访问时生效。)例如:
>>> class Animal: ... def run(self): ... print('Animal is running') ... >>> class Dog(Animal): ... def run(self): ... print('Dog is running') ... >>> d = Dog() >>> d.run() Dog is running >>> Animal.run(d) Animal is running
Animal.run(d)
直接调用基类的方法。
Python 也支持多重继承方式。派生类具备多个基类的定义方式以下:
class DerivedClassName(Base1, Base2, Base3): <statement-1> . . . <statement-N>
在最简单的状况下,搜索从基类继承属性的操做是深度优先,从左到右,当存在层次结构重叠的状况下,是不会在同一个类中搜索两次的。具体可能的状况:当某一个属性在 DerivedClassName 中没有找到,则会在基类 Base1 中先查找,而后递归到 Base1 的基类中去查找,如果未找到,则搜索 Base2,依此类推。
但真实的状况可能会复杂一些:方法解析顺序会动态改变以支持对 super()
的协同调用,具体的细节能够参考下面的资料
https://www.python.org/download/releases/2.3/mro/
其实严格意义上的仅限从一个对象内部访问的“私有”实例变量在 Python 中是并不存在的。可是,大多数 Python 代码都会遵循这样的一个约定:带有一个下划线的名称(例如 _spam
) 应该被看成是 API 的非公有部分(不管它是函数、方法仍是数据成员)。
在避免名称与子类所定义的名称冲突,Python 存在这对此种机制的支持,被称为名称改写。形如 __spam
这样的标识符(至少带有两个前缀下划线,至多一个后缀下划线)将被替换为 _classname__spam
,其中 classname
为去除前缀下划线的当前类名称。
这种名称改写的支持可以有助于子类重载方法而不破坏类内方法调用。例如:
class Mapping: 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 = update class MappingSubclass(Mapping): def update(self, keys, values): # 为 update() 提供新的特征 # 可是不破坏 __init__() for item in zip(keys, values): self.items_list.append(item)
在上面的例子当中,即使 MappingSubclass
引入一个 __update
标识符的状况下,也不会出错,由于在 Mapping
中它已经被替换为 _Mapping__update
,在 MappingSubclass
则被替换为 _MappingSubclass__update
。
在这里须要注意:改写规则的设计主要是为了不冲突。这也使得修改或访问这些被视为私有的变量成为可能。在某些特殊的状况下,可能颇有用。但同时也须要注意,若非必要,不要直接访问或修改这些被视为私有的变量。
以上就是关于类(Class)继承和私有变量的内容。
欢迎关注微信公众号《书所集录》