首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python面向对象编程-面向对象的各种姿势及实例化

Python面向对象编程-面向对象的各种姿势及实例化

作者头像
1846122963
发布2018-07-26 16:19:15
6700
发布2018-07-26 16:19:15
举报

前文回顾

前面我们讲解了面向对象的基础,没有涉及到一行代码。这些看似很枯燥的概念,其实是非常重要的。如果不能及时理解或掌握,可以慢慢来。

面向对象更进一步的抽象了世界。OOP的世界观:

  1. 世界是由对象组成的
  2. 对象具有运动规律和内部状态
  3. 对象之间可以相互作用

面向对象的特性:

  1. 唯一性:对象都是唯一的,不存在两个相同的对象,除非他们是同一个对象。
  2. 分类性:对象是可分类的,世界是由不同的类型组成的。

面向对象的三大特征:

  1. 封装
  2. 继承
  3. 多态

面向对象最重要的概念就是类(Class)和实例(Instance),必须牢记类是抽象的模板,而实例则是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据有可能不同。

在Class内部,可以有属性和方法,而外部代码可以通过直接调用实例变量的方法来操作数据,这样,就隐藏了内部的复杂逻辑。

面向对象的本质:对行为和数据的封装;有时候数据就是数据;而有的时候行为就是行为。我们先使用Python标准库中的namedtuple来实现一个入门的类吧。目的是为了组织数据。命名元组的优势:组织的更好,字段有名字。

  from collections import namedtuple

  Door = namedtuple('Door', ['number', 'status'])

  # 实例化
  door = Door(10010, 'closed')
  print(door.status)
  print(door.number)

: closed
: 10010

以面向对象的方式实现Door,

  class Door:
      def __init__(self, number, status):
          # . 用于访问对象的属性与方法
          self.number = number
          self.status = status

  door = Door(10010, 'closed')  # 调用初始化方法(其他语言中的构造方法)
  print(door.number)  # 获取属性,输出:10010
  print(door.status)  # 获取属性,输出closed

类就是数据与逻辑(或动作)的集合。上述的Door类中只有数据没有逻辑,那么我们在该类中加入开门与关门的动作,用来操纵类中的数据。上述的例子改写如下:

class Door:
    def __init__(self, number, status):
        self.number = number
        self.status = status

    def open_door(self):
        self.status = 'opened'

    def close_door(self):
        self.status = 'closed'


door = Door(10010, 'opened')

print("door's number is: {}".format(door.number))
print("door's status is: {}".format(door.status))

print("现在关门做点坏事")
door.close_door()
print("door's status is: {}".format(door.status))

print("坏事做完,开启门窗透透气吧")
door.open_door()
print("door's status is: {}".format(door.status))

执行上述代码:

$ python3 door.py
door's number is: 10010
door's status is: opened
现在关门做点坏事
door's status is: closed
坏事做完,开启门窗透透气吧
door's status is: opened

上述代码中,我们通过open_door()close_door()函数来操作了Door类的status数据。

如果大家写过C++Java代码,可以很轻松地用C++Java进行实现。我们看看C++是如何实现上述代码的:

// filename: door.cpp
#include <iostream>

using namespace std;


class Door
{
public:
    int number;
    string status;
    
    Door(int number, string status)
    {
        this->number = number;
        this->status = status;
    }

    void open_door(void);
    void close_door(void);
};

void Door::open_door(void)
{
    this->status = "opened";
}

void Door::close_door(void)
{
    this->status = "closed";
}


int main(int argc, char *argv[])
{
    Door door(10010, "opened");

    cout << "door's number is: " << door.number << endl;
    cout << "door's status is: " << door.status << endl;

    cout << "现在关闭门窗做点坏事" << endl;
    door.close_door();
    cout << "door's status is: " << door.status << endl;

    cout << "坏事做完,开启门窗透透气吧" << endl;
    door.open_door();
    cout << "door's status is: " << door.status << endl;
    
    return 0;
}

编译并运行。结果如下:

$ g++ door.cpp -o door
$ ./door
door's number is: 10010
door's status is: opened
现在关闭门窗做点坏事
door's status is: closed
坏事做完,开启门窗透透气吧
door's status is: opened

我们知道,Java是源自于C++的。那么我们看看如何用Java代码该怎么写呢?

// filename: Door.java
class DoorConstructor {
    int number;
    String status;
    
    DoorConstructor(int number, String status) {
        this.number = number;
        this.status = status;
    }

    public void close_door() {
        this.status = "closed";
    }

    public void open_door() {
        this.status = "opened";
    }
}

public class Door {
    public static void main(String args[]) {
        DoorConstructor door = new DoorConstructor(10010, "opened");
        System.out.println("door's number is: " + door.number);
        System.out.println("door's status is: " + door.status);

        System.out.println("现在关门做点坏事");
        door.close_door();
        System.out.println("door's status is: " + door.status);

        System.out.println("坏事做完,开启门窗透透气吧");
        door.open_door();
        System.out.println("door's status is: " + door.status);
    }
}

编译并运行:

$ javac Door.java
$ java Door
door's number is: 10010
door's status is: opened
现在关门做点坏事
door's status is: closed
坏事做完,开启门窗透透气吧
door's status is: opened

我们看看Go语言是如何使用面向对象的。先看代码:

package main

import "fmt"

type Door struct {
    number int
    status string
}

func (d *Door) close_door() {
    d.status = "closed"
}

func (d *Door) open_door() {
    d.status = "opened"
}

func main() {
    door := Door{10010, "opened"}

    fmt.Println("door's number is:", door.number)
    fmt.Println("door's status is:", door.status)

    fmt.Println("现在关门做点坏事")
    door.close_door()
    fmt.Println("door's status is:", door.status)

    fmt.Println("坏事做完,开启门窗透透气吧")
    door.open_door()
    fmt.Println("door's status is:", door.status)
}

编译并运行:

$ go build door.go 
$ ./door 
door's number is: 10010
door's status is: opened
现在关门做点坏事
door's status is: closed
坏事做完,开启门窗透透气吧
door's status is: opened

上面我们通过四种支持面向对象的编程语言(当然还有很多编程语言,小白并没有拿它们一一举例。),简单地演示了这些语言的基本套路。这里所举的例子都是入门级的,限于小白的水平也做不到深入。举这些例子的目的是想告诉大家:面向对象编程只是一种思想,掌握了编程思想,那么使用什么样的语言来完成你的当前的任务就看这门语言提供了哪些特性、自己对这门语言的理解及熟练程度。

实例化的过程

接下来会通过一些具体的实例说明实例化的过程。

In[14]: class Heap:
    ...:     def __init__(self):  # 此函数通常叫做构造函数,在Python中更多叫做初始化函数,在对象创建完成后会立刻执行
    ...:         self.data = []

    ...:     def add(self, x):  # 第一个参数是self,其他参数与一般函数定义一样
    ...:         self.data.append(x)

    ...:     def pop(self):
    ...:         if self.data:
    ...:             self.data.pop()
    ...:         else:
    ...:             print('heap is empty')
    ...:             

In[15]: heap = Heap()  # 实例化Heap类,实例为heap

In[16]: heap.data
Out[16]: []

In[17]: heap.add(3)

In[18]: heap.add(4)

In[19]: heap.add(5)

In[20]: heap.data
Out[20]: [3, 4, 5]

In[21]: heap.pop()

In[22]: heap.pop()

In[23]: heap.data
Out[23]: [3]

In[24]: heap.pop()

In[25]: heap.data
Out[25]: []

In[26]: heap.pop()
heap is empty

上面代码中的self代表heap这个实例。当然,代码中的self并不一定要写为self,还可以是其他的非关键字。

再来一个例子:

$ cat person.py
class Person:  # 创建一个名为Person的类
    def __init__(self, name, job=None, pay=0):  # 初始化函数接收三个参数,与一般的函数参数具有相同意义
    self.name = name  # 创建对象时填充这些字段
    self.job = job  # self就是将要创建的对象(或实例)
    self.pay = pay

bob = Person('Bob Smith') # test the class
sue = Person('Sue Jones', job='dev', pay=10000) # 自动执行__init__方法
print(bob.name, bob.pay) # 获取对象的属性
print(sue.name, sue.pay) # 不同的对象其自身的数据不一定相同

尽管上面的Person类非常简单,不过它依然演示了一些重要的内容。我们注意到bob的name并不是sue的name,并且sue的pay不是bob的pay。bob和sue它们都是两个独立的信息记录。从技术的角度来看,bob与sue都是namespace objects,就像其他所有的类实例一样,它们创建时都有各自独立的状态信息的拷贝。因为每个类的实例都有自己self属性的集合,可以把类可以理解为一个蓝图、工厂或模具。

一个示例,

class Door:
    def __init__(self, number, status):
        self.number = number
        self.status = status

    def open(self):
        self.status = 'opened'

    def close(self):
        self.status = 'closed'

door = Door(1, 'closed') # 看起来非常像一个函数调用。事实上,
                         # 确实发生了一些函数调用,它调用了__init__函数,
                         # 第一个参数由解释器自动传入,表示实例本身,
                         # 通常命名为self,也可以为其他非关键字
print(door.__class__)
print(Door.__class__)
print(type.__class__)
# 所有类,都是type或者type的子类的实例

: <class '__main__.Door'>
: <class 'type'>
: <class 'type'>

__init__函数并不会创建对象,__init__函数初始化对象。对象(或实例)创建过程为:

  1. 首先创建对象
  2. 对象作为self参数传递给__init__函数
  3. 返回self

实例怎么来的?由类的__new__方法实现。如果要改变默认创建默认的创建实例的行为,可以写__new__方法,不过通常是不写的。

class Door:
 #    def __new__(cls): # 创建实例的,可以改变实例创建的行为,这就是元编程的体现
 #        pass

    def __init__(self, number, status):
        self.number = number
        self.status = status

    def open(self):
        self.status = 'opened'

    def close(self):
        self.status = 'closed'

door = Door(1, 'closed') # 看起来非常像一个函数调用。事实上,
                         # 确实发生了一些函数调用,它调用了__init__函数,
                         # 第一个参数由解释器自动传入,表示实例本身,
                         # 通常命名为self
print(door.__class__)
print(Door.__class__)
print(type.__class__)
# 所有类,都是type或者type的子类的实例

: <class '__main__.Door'>
: <class 'type'>
: <class 'type'>

实例化的时候,传递的参数列表是__init__方法除了第一个参数之外的所有参数,支持函数的所有参数变化。

当没有显式的定义__init__方法的时候,会使用默认的__init__方法,

def __init__(self):
    pass

通过.操作符访问实例的属性或者调用实例的方法。当我们调用实例方法的时候,第一个参数即实例本身,由解释器自动传入。

总结

今天就到此为止吧,要写的内容太多了。内容写的太长的话,很多小伙伴估计会看得不耐烦。

预告一下明天的内容:

  1. 类的作用域
  2. 静态方法/类方法
  3. 访问控制
  4. 封装
  5. 继承
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-05-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 小白的技术客栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前文回顾
    • 实例化的过程
    • 总结
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档