xuzhengfu / pilot

进入编程世界的第一课
1 stars 0 forks source link

p1-9-oo-3 第九章 理解对象与类:Python 篇 #27

Open xuzhengfu opened 4 years ago

xuzhengfu commented 4 years ago

理解对象与类:Python 篇

1. 了解 “面向对象概念” 在 Python 中的具体实现

在前面大套的 历史背景 和 理论概念解说 之后,我们来看看 Python 中对面向对象概念的具体实现,主要结合一些代码例子进行说明。

2. 了解 类定义与对象创建

class Dog:

    kind = ''

    def __init__(self, name):
        self.name = name

    def bark(self):
        return 'woof-woof'

a = Dog('Fido')
b = Dog('Buddy')
  1. 第一行是关键字 class 打头,代表 类定义 的开始,后面是 类的名字,然后是一个 冒号,表示下面缩进的代码段是 Dog 类的定义;在 Python 中类本身也是个对象,叫 类对象(class object)

  2. 下面是一个在 类定义 里直接出现的变量 kind,这样定义的变量称为 类变量(class variable),这种变量是整个类共有的,所有该类的 对象实例 共享;

  3. 下面是一个函数 __init__() 的定义,因为这个函数定义在类里,所以通常我们称之为 方法(method)

  • 方法的第一参数必定是 self,这是个特殊的变量,代表从 这个类 实例化 出来的对象自己;注意这里是类的定义,还没有真正创建出对象,这个 self 相当于对未来产生对象的 “提前引用”;类定义中的方法在被调用时解释器都会自动传递这个 self 进去;

  • 作为一种约定俗成的规矩,类似 __init__() 用双下划线开头和结尾的方法,属于特殊方法(special method),是 Python 解释器特别定义和使用的;

  • __init__() 是解释器在 实例化 一个对象之后自动调用的方法,用来对 新创建的对象实例 做初始化;

  • 方法可以在 self 之后带任意的输入参数,这些输入参数由方法自行使用;

  • self.name 定义了一个 实例变量(instance variable),前面说了 self 代表未来被 实例化 的 对象 自身,. 表示属于对象的,name 则是这个实例变量的名字,这个方法用传进来的参数 name 来初始化实例变量 self.name,要注意这两个 name 是不一样的;

  1. 下面是函数 bark() 的定义,这是我们自定义的方法,没有自定义的输入参数(例行的 self 参数不算),然后返回 dog 的叫声;
  1. 缩进的 类定义部分 结束,下面是创建这个类的 对象实例 的方法,a = Dog('Fido')
  • 一个类的实例化,就是用这个类作为模板创建一个实际存在的对象,Python 的语法是把类对象 Dog 当做一个函数来使用,Dog('Fido') 看上去就是个函数,它的意思是:创建一个 Dog 类的 对象实例(instance object),并用参数 'Fido' 调用类的 __init__() 方法;

  • 解释器会在内存中创建这个对象实例,然后用参数 'Fido' 调用类的 __init__(self, name) 方法,第一个参数 self 解释器会自动放进去(就是刚刚创建好的对象实例),而 name 参数就是 'Fido',__init__() 方法运行完毕就会把这个对象的 self.name 实例变量设为 'Fido';

  • 实例化 执行完毕,将创建的对象赋给 a 变量。

  1. 创建第二个对象实例 b。

  2. 创建出来的两个 对象实例 就可以使用了。

如果前面的看上去有点复杂,可以先 努力理解 和 记住 下面的几个要点:

  • 可以用 class MyClass: 的语法来定义类;
  • 类定义中 直接定义的变量是 类变量(class variable),为所有 对象实例 所共享,可用 MyClass.xxx 的语法访问;
  • 类定义中出现的 self.xxx实例变量(instance variable),是各个 对象实例 各自的属性(attribute),互不影响,对象实例创建后可以用 obj.xxx 的语法访问;
  • 类定义中通常会定义 初始化方法 __init__(self, param1, param2, ...),该方法会在 对象实例 创建后自动被调用,其参数中 self 是固定的,而后面如果有其他参数,需要在创建 对象实例 时传给类对象(MyClass),类似这样:obj = MyClass(param1, param2, ...),这两个地方的参数表是对应上的;
  • 类定义中 定义的函数是 对象实例 的方法(method),可以用 obj.method() 的语法调用;所有方法的第一个参数固定为 self,其他参数的使用方法与前述 __init__ 方法一样。

代码略。 和前面的 狗类 好像没有太大区别,因为从特征上来看,基本一致:无论是猫还是狗,都有一个 类变量 kind(动物的分类),以及一个实例变量 self.name(动物的名字),然后有个 方法 来发出叫声。

3. 了解 “父类、子类、继承与多态” 的实现

我们要定义一个比猫和狗都更加抽象的类 —— Animal,作为猫和狗的共通父类,这个类要把上面说的共性特征都表达出来。

class Animal:

    kind = ''

    def __init__(self, name):
        self.name = name

    def voice(self):
        return '...'

Animal 类具有类变量 kind、实例变量 self.name 和一个方法 voice(),但是没有给出具体的分类名和叫声是什么。

在 Python 中要继承一个已存在的类,使用下面这样的语法:

class Dog(Animal):

    kind = 'canidae'

    def voice(self):
        return 'woof-woof'

    bark = voice

class 语句中 类名后面可以紧跟一个括号,里面是要继承的父类,这样定义出来的子类就直接拥有了父类的一切,但可以修改,上面的 类定义 就修改了 类变量 kind 的值,给出了 voice() 方法的一个 狗类实现(“woof-woof”),并定义了一个自己独有的方法 bark(作为 voice 方法的一个别名)。

a = Animal('キュゥべえ')
d = Dog('Snoopy')
for animal in [a, d]:
    print(animal.name, ':', animal.voice())

仔细看上面的代码,a、d 虽然是不同类的对象实例,但是,因为它们是 Animal 或其子类的对象实例,所以拥有 Animal 类所定义的标准接口(继承),从而才可以在一个循环中一视同仁地处理,而且不同类的 对象实例 自动调用了各自 类定义的 voice() 方法实现,产生各自正确的输出(多态)。

4. 了解 公有和私有

和 C++、Java 等面向对象编程语言不一样,Python 并没有私有成员一说,从语法上讲,所有类定义中出现的类变量、对象变量、方法都是公开的。

尽管 Python 并没有私有成员一说,但是 Python 给出了一个 “弱约束” —— 一种约定俗成的代码风格:将类定义中所有下划线 _ 开头的变量和方法作为 “内部” 变量和方法来看待,也就是说,这些变量和方法属于类的内部实现的一部分,随时可能改变,外部最好不要依赖于这些变量和方法。

所以 Python 虽然没有提供强制受限的 “私有成员”,但还是遵循了 “接口与实现分离” 的思维模式,我们在定义一个类的时候,如果有不希望外部直接使用的变量和方法,可以在其名字前加一个下划线 _

小结

  • 了解在 Python 中如何 定义类 和 创建类的对象实例;
  • 了解在 Python 中的继承和多态的实现方法。
  • 现在可以再读一遍前一章的概念,应该会有不同的感受。

Logging

2020-02-23 23:09:13 initialize