Python 类(Class)| 二

类(Class)| 二


关于类的基础内容,包括类的定义,类对象,实例对象,方法对象,类和实例变量。在下面的这篇文章已经有基本的介绍:

https://blog.csdn.net/weixin_45642918/article/details/104579523

今天这篇文章主要介绍类的继承以及私有变量。

继承


Python 支持类的继承。下面是派生类的定义:

class DerivedClassName(BaseClassName):
    <statement-1>
    .
    .
    .
    <statement-N>

在这里基类 BaseClassName 与派生类必须定义在同一个作用域中。也允许其他任意表达式代替基类名称所在的位置。例如,基类定义在另一个模块中时:

class DerivedClassName(modname.BaseClassName):

派生类跟基类执行过程是一样的。当构造类对象的时候,基类其实是会被先记下。当解析属性引用的时候:如果请求属性在派生类中没有找到时,将会从基类中进行搜索。若是继承的基类也是派生自其他类,则递归应用此规则。

派生类的实例化也没有太特殊的地方: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 中存在这个方法,产生函数对象,引用生效。返回的结果则是基类方法中的内容。

派生类是可以重载基类的方法。因为当调用同个对象的其他方法时,基类方法是没有其他特殊权限的。所以基类的方法调用同一基类中定义的另一个方法可能最终会调用覆盖它的派生类的方法。

在上面的例子中,Dog 并没有相应的方法,现在也编写一个同样名为 run 的方法,再看调用结果:

>>> 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)继承和私有变量的内容。

欢迎关注微信公众号《书所集录》
阅读 252

推荐阅读