python面向对象编程 -- 封装继承(python3.5)

Posted Wadirum

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python面向对象编程 -- 封装继承(python3.5)相关的知识,希望对你有一定的参考价值。

 面向对象编程三要素:封装、继承和多态。本文主要看和封装、继承相关的概念;在python中多态的概念比较模糊,本文不做讨论。

1 封装

封装:将数据和操作组装到一起,对外只暴露一些接口供类外部或子类访问,隐藏数据和操作的实现细节。

在其他面向对象语言,比如java中,属性访问控制一般有三种状态:private、protectd、public。python中没有什么东西是完全不可见的,没有任何机制可以强制性的隐藏数据。所以在python中不存在真正的只能在对象内部访问的属性。
一个被大多数的python程序员遵守的约定:以下划线开头的变量应该被当作非公有属性对待,即它应该被当作是实现细节,其修改应该是不被察觉的。

1.1 名称管理

python中有一种机制对类成员的私有化提供有限的支持,称为名称管理
任何以至少两个下划线开头、至多一个下划线结尾的标识符,如__spam,都会被转换为_classname__spam的形式,其中classname是当前的类名。这种名称管理机制不考虑标识符实际归属哪个命名空间,只要它发生在类的定义中,就会自动生效。

class Chinese:
    """A sample example class"""
    nationality = China

    def __init__(self, name, age, gender):
        self.name = name
        self.__age = age
        self.gender = gender

    def __str__(self):
        return {}(name={}, age={}, gender={}).format(self.__class__, self.name, self.__age, self.gender)

xm = Chinese(xiaoming, 18, male)
print(xm.__dict__)  # {‘gender‘: ‘male‘, ‘name‘: ‘xiaoming‘, ‘_Chinese__age‘: 18}
print(xm)           # <class ‘__main__.Chinese‘>(name=xiaoming, age=18, gender=male)
print(xm.__age)     # AttributeError: ‘Chinese‘ object has no attribute ‘__age‘
  • __age在类中被自动转化为_Chinese__age保存在实例的命名空间中;
  • 类内部出现的__age形式的标识符都会被自动转换,所以__str__方法可以访问本例中的__age属性;
  • 在类外部(包括在子类中),无法访问__age属性,因为实例命名空间中并不存在该属性。 

继承

继承:对现有类的一种复用机制。如果相对现有的类做一些个性化的修改,可以通过继承实现,而不是直接修改原类。

python的继承语法如下:

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

BaseClassName代表被继承的类,称为基类或者父类。
DerivedClassName称为子类。除了添加了父类,子类的定义和实例化和普通的类并没有什么区别。可以通过BaseClassName.__bases__查看其父类。

  1. 子类可以引用父类命名空间中的所有属性。属性的查找顺序:实例-->子类-->父类-->父类的父类...-->object。python3中所有的类都隐性继承自object类。
  2. 子类可以重写父类的属性。一旦属性重写,对于子类或子类之前开始的属性查找,父类对应的属性相当于被屏蔽掉了。
  3. 如果子类要重写初始化方法,最好通过扩展父类的初始化方法实现,即调用父类的初始化方法,并实现自己的个性化扩展。
class Animal:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def reply(self):
        return self.speak()

    def speak(self):
        return Hello world

class Cat(Animal):
    def __init__(self, name, age, breed):
        super().__init__(name, age)
        self.breed = breed

    def speak(self):
        return Cat Miaow

print(Cat.__bases__)    # (<class ‘__main__.Animal‘>,)
garfield = Cat(Garfield, 10, Garfield)
print(garfield.reply()) # Cat Miaow

super().__init__引用Animal之前的祖先类的初始化方法。

多继承

python是支持多继承的,其语法如下:

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

多继承要解决的主要问题是属性搜索顺序:
1. 将类及其祖先类按照搜索优先级从高到低排列生成一个列表的过程称为类的线性化。
2. MRO: Method Resolution Order, 指用于类线性化的规则,python3用的是C3算法。C3算法可以保证线性化的单调性,单调性是指:如果在类C的线性化中,C1的优先级高于C2,那么C的所有子类的线性化中,C1的优先级高于C2。
3. 可以通过 DerivedClassName.__mro__ 查看类的线性化结果。

想了解C3算法的具体实现,可以参考文章:https://www.python.org/download/releases/2.3/mro/

其他

数据属性和方法属性可能会出现标识符冲突,这会导致非预期的属性重写(对于标识符而言,赋值即定义),这在大型项目中会引起难以定位的bugs。为了避免标识符冲突,使用一些约定最小化冲突概率是明智的。参考方案:

  • 大写方法属性的首字母,给数据属性标识符添加特定的前缀
  • 使用动词标识方法属性,使用名词标识数据属性

参考:

1 file:///Library/Frameworks/Python.framework/Versions/3.5/Resources/English.lproj/Documentation/tutorial/classes.html











以上是关于python面向对象编程 -- 封装继承(python3.5)的主要内容,如果未能解决你的问题,请参考以下文章

python3 面向对象编程--类的封装和继承

面向对象编程思想 以及 封装,继承,多态 和 python中实例方法,类方法,静态方法 以及 装饰器

python--面向对象之三个特性:封装继承多态

python面向对象(封装继承多态)+ 面向对象小栗子

python 全栈 python基础 (十六)面向对象编程的 继承 多态与多态性 封装

面向对象编程语言特征:封装继承多态