1. 面向对象编程

OOP ( Object  Oriented Programming) 即面向对象编程。

面向对象编程是一种编码思想,或是一种代码组织方式。如同编辑文章时,可以选择分段、分节的方式让文章看起来有层次、更方便阅读或修改。

编码时可以选择使用 OOP 方案,也可以选择不使用。如同行文一样,使用或不使用都不会对核心逻辑产生影响。

面向对象编程有自己的核心编码理论,对于任何一种计算机语言而言,如果选择支持此理论,则称此计算机语言支持面向对象编程。如 C++、Java、Python……

因每一种计算机语言语法上的差异性,在提供 OOP 实现时的语法规范会有很大的区别。除此之外,对于每一种语言而言,也可以在 OOP 基础理论上进行语法扩展或限制。如 Python 支持多继承。而 Java 语言只支持单根继承……

1.1 OOP 特点

要了解 OOP 的特点,可从 2 个角度进行阐述。

广义角度:让程序像人类解决问题一样去解决问题,让程序具有人的思维模式。

人类解决问题时,先是要了解问题域中会涉及到哪些对象,然后再深入了解每一个对象的特性或功能,最后再做出相应的决策。

比如:为班级选一名班长。

选班长就是现实世界的一个问题域,如何才能选择一名符合要求的班长?

  1. 首先确定此问题中涉及的对象(此处便是班上的所有学生)。
  2. 然后了解每一个学生的兴趣、爱好、性格……以及个人能力等等。
  3. 从了解的群体中匹配一个符合班长标准的学生便可。

面向对象编程中的对象一词,便是借鉴了现实世界中对象概念。

狭义角度:OOP 编码为整个程序维护带来的优势

OOP 组织的代码可让程序整体上有高度的可阅读性,除此之外,最主要的特点是可提高代码的复用性、安全性、可扩展性。

任何事情都会 2 面性,OOP 会增加代码的理解难度。

1.2 OOP 基本概念

OOP 中有两个很重要的概念,类和对象

对象从何而来?

现实世界中我们很少思考这个问题,在选班长时,不会思考学生是从哪里来的,即使思考这个问题,也会认为那是哲学家的事情。

我们不思考现实世界中的手机、电脑、电视机是怎么来的……因为我们不关心这个,我们关心的是使用它们所提供的功能。

如果我们思考一下手机是怎么出现的,则会发现:

  1. 首先需要工程师设计手机蓝图。
  2. 在工厂里根据手机蓝图进行生产(可能生产很多)。
  3. 用户购买手机,了解手机特性和功能,使用手机。

我们身边的诸如 电视机、洗衣机、电脑……无不例外的需要经过这几个过程后方能来到我们的世界。

即使是人也是女娲按自己的样子创建出来的……

同理,电脑世界里不会突然冒出手机、电脑、学生……如何才能让电脑出现此类对象。一样,先设计一个蓝图,此蓝图在电脑世界我们就称其为“类”

有了“类”之后才可以创建手机对象,有了对象后才能在程序代码中使用设计时为手机赋予功能完成程序逻辑。

现实世界设计手机蓝图时,需要设计手机的外观,如大小、形状、体重……需要赋予手机功能、如打电话、播放音乐、播放视频、上网……

在计算机的编码世界里,同样在设计类时需要为 “手机类” 设计外观和功能。OPP 中称外观为属性,称功能为方法。

类是蓝图,具有抽象性特征

对象是根据蓝图创建出来的个体,具有具体性、实用性特征

2. Python  实现 OOP

如需使用 OOP 理念实现程序逻辑,则需遵循如下流程:

2.1 分析问题

首先需要明确问题:如编写一个程序摸拟小狗的行为。

此问题中的对象便是小狗,所以程序中需要一只小狗。

按上所述,创建小狗之前需要设计“狗类”,因此需要为类的设计提供足够的信息。

分析可得在设计类时需要有小狗属性:姓名、年龄,小狗的行为:尊下、打滚。

2.2 类设计语法

class Dog():

    def __init__(self, name, age):
"""初始化属性name和age"""
self.name = name
self.age = age def sit(self):
"""小狗蹲下行为"""
print(self.name.title() + " 乖乖的尊下了!") def roll_over(self):
"""小狗打滚"""
print(self.name.title() + " 开始打滚哈!")

 如上为 python 中类设计的结构语法:

  • 类的函数称为方法,方法的第一个参数须是 self 关键字。
  • __init__ 方法是必须的,其方法名不得修改。此方法会在创建对象时被自动调用,用来初始化对象数据。
  • self.name 声明一个对象变量,此变量会保存对象的数据。

2.3 创建对象语法

有了类后,方可创建对象,有了对象后方可激活属性和方法。

my_dog = Dog('小雪', 6)
print("小狗的名字:"+my_dog.name.title()+".")
print("小狗今年"+str(my_dog.age)+" 岁了")
my_dog.sit()
my_dog.roll_over()

创建小狗时,需调用和类名相同的方法,如上述的 Dog( ) 方法,此方法也叫构造方法,此方法实质是调用了类设计中的 __init__ 方法。所以需要传递小狗的具体姓名和年龄初始 name 和 age 变量。

调用类中的方法时,不需要为方法声明时的 self  参数传递值。

有了对象后,如需要使用此对象的数据时,可使用 .  运算符。如上  my_dog.name 得到小狗的姓名。

当然,在创建小狗后,也可以根据需要修改小狗的姓名和年龄。

my_dog.name='小花'
my_dog.age=4

同样,也可以使用 . 运算符调用类设计时的方法。调用方法也不需要为第一个参数 self 传值。

运行结果:

小狗的名字:小雪.
小狗今年6 岁了
小雪 乖乖的尊下了!
小雪 开始打滚哈!

有了类之后,可以根据此类的设计方案,创建出多个对象。每一个对象有自己的数据空间,彼此之间的数据是独立且隔离的。

my_dog = Dog('小黑', 6)
your_dog = Dog('小白', 3)
print("我的小狗的名字: "+my_dog.name.title()+".")
print("我的小狗的年龄 "+str(my_dog.age)+"岁了.")
my_dog.sit()
print("\n你的小狗的名字: "+your_dog.name.title()+".")
print("你的小狗的年龄 "+str(your_dog.age)+" 岁了.")
your_dog.sit()

 如同现实世界一样。现在有了 2 只小狗,它们是独立的个体。修改其中一只狗的名字,对另一只小狗是没影响的。

我的小狗的名字: 小黑.
我的小狗的年龄 6岁了.
小黑 乖乖的尊下了! 你的小狗的名字: 小白.
你的小狗的年龄 3 岁了.
小白 乖乖的尊下了!

3. OOP 的封装性

封装性可以从 2 个角度上展开讨论:

3.1 广义角度:无处不封装

类就是一个封装体:它把数据以及对数据的相关操作方法封装在了一起。

方法也是一个封装体:封装了代码逻辑

封装的优点!

当我们通过对象使用数据和方法时,不需要了解其中的内部细节,如此实现了设计和使用的分离,和现实世界中我们使用手机一样,不需了解手机的内部结构和细节。

开发者在使用 python 提供的模块时,不需要了解模块中的相关实现细节,直接使用其功能便可。

设计和使用的分离能加速工业软件的开发效率。

3.2 狭义角度:保证内部数据的完整性

创建一只小狗后,可以编写如下代码修改小狗的年龄。

my_dog = Dog('小雪', 6)
my_dog.age=-4

显然这是不符合实际情况的,没有一只小狗的年龄可以是负 4 岁。但是,现在程序可以正常运行。

小狗今年-4 岁了

出现这样不合常理的想象,应该追究谁的责任。类的设计者还是对象使用者?

我们应该要追究类设计者的责任,就如同我刚买的手机不能充电一样,是设计者的设计缺陷引起的。

我们应该在设计类的时候提供一种内部安全检查机制,保护变量能被赋予一个正确的、可靠的值。

实施流程:

1. 在变量、方法的前面加上双下划线(__)让变量成为私有概念

python 的语法有很大的弹性。添加下划性只是一种象征性或类似于道德层面的约定。并不能真正意义上让外部不能访问。

class Dog():

    def __init__(self, name, age):
"""初始化属性name和age"""
self.name = name
#私有化
self.__age = age def sit(self):
"""小狗蹲下行为"""
print(self.name.title() + " 乖乖的尊下了!") def roll_over(self):
"""小狗打滚"""
print(self.name.title() + " 开始打滚哈!")

2.  在类中提供对应的 set 和 get 方法实现对内部变量的保护。

    def get_age(self):
return self.__age

# 对数据进行检查
def set_age(self, age):
if age<0:
print("小狗的年龄不可能为负数")
return
self.__age = age

3. 测试

my_dog = Dog('小雪', 6)
my_dog.set_age(-4) print("小狗的名字:"+my_dog.name.title()+".")
print("小狗今年"+str(my_dog.get_age())+" 岁了")

输出结果

小狗的年龄不可能为负数
小狗的名字:小雪.
小狗今年6 岁了

python 还有一种更优雅的解决方案。使用注解方式。

class Dog():

    def __init__(self, name, age):
self.name = name
# 私有属性,属性名(age)前面双下划线的名称
self.__age = age # 实例方法
def run(self):
print("{} 在跑……".format(self.name)) # 使用 @property 定义age属性的 get 方法
@property
def age(self):
return self.__age # 使用 @age.setter 定义 age 属性的 set 方法必须放在@property的后面
@age.setter
def age(self, age):
if age < 0:
print("小狗的年龄不能是负数")
return
self.__age = age #实例化小狗
dog = Dog("小红", 3)
print("{0} 狗狗的年龄是 {1}".format(dog.name, dog.age))
#修改年龄
dog.age = -4
print("{0} 狗狗的年龄是 {1}".format(dog.name, dog.age))

输出结果

小红 狗狗的年龄是 3
小狗的年龄不能是负数
小红 狗狗的年龄是 3

 

4 . 总结

面向对象编程可以用《人类简史》中的一句话总结,人类文明的进步不一一定能泽福到每一个个体。

类可以设计的很完美,但每一个对象做为个体可以有自己的命运。

封装是面向对象编程理念中最基本也是最重要的特性,没有封装便没有后续的更多。

封装可能让我们把相关联的数据与方法构建成一个逻辑上的整体,也可保护内部数据的安全性,毕竟没有数据安全性的程序是没有意义的。

Python 面向对象编程之封装的艺术的更多相关文章

  1. python基础-面向对象编程之封装、访问限制机制和property

    面向对象编程之封装 封装 定义:将属性和方法一股脑的封装到对象中,使对象可通过"对象."的方式获取或存储数据. 作用:让对象有了"."的机制,存取数据更加方便 ...

  2. Python 面向对象编程之进阶使用

    我们在https://www.cnblogs.com/yinsedeyinse/p/9976280.html中学习了面向对象的编程方法.现在学习他的进阶用法. 1. 静态方法 2. 类方法 3. 属性 ...

  3. python面向对象编程之组合

    前面讲了面向类与对象的继承,知道了继承是一种什么"是"什么的关系. 然而类与类之间还有另一种关系,这就是组合 先来看两个例子: 先定义两个类,一个老师类,老师类有名字,年龄,出生的 ...

  4. java学习笔记(基础篇)—面向对象编程之封装、继承、多态

    一. OOP中的基本概念 Java的编程语言是面向对象的,采用这种语言进行编程称为面向对象编程(Object-Oriented Programming, OOP), 它允许设计者将面向对象设计实现为一 ...

  5. JS面向对象编程之封装

    来源:https://segmentfault.com/a/1190000015843072 我们所熟知的面向对象语言如 C++.Java 都有类的的概念,类是实例的类型模板,比如Student表示学 ...

  6. python基础-面向对象编程之继承

    面向对象编程之继承 继承的定义:是一种新建类的方式,新建的类称之为子类或派生类,被继承的父类称之为基类或超类 继承的作用:子类会""遗传"父类的属性,从而解决代码重用问题 ...

  7. python基础-面向对象编程之反射

    面向对象编程之反射 反射 定义:通过字符串对对象的属性和方法进行操作. 反射有4个方法,都是python内置的,分别是: hasattr(obj,name:str) 通过"字符串" ...

  8. python基础-面向对象编程之多态

    面向对象编程之多态以及继承.抽象类和鸭子类型三种表现形式 多态 定义:同一种类型的事物,不同的形态 作用: 多态也称之为"多态性".用于在不知道对象具体类型的情况下,统一对象调用方 ...

  9. python基础-面向对象编程之组合

    面向对象编程之组合 定义:一个对象中拥有另一个或其他多个对象的属性和方法. 作用:减少代码的冗余,降低耦合度 关于耦合度的说明 耦合:通俗地讲,就是相互作用,相互影响的意思 耦合度越高,程序的可扩展性 ...

随机推荐

  1. 【Java】注解Annotation

    注解 Annotation 理解Annotation jdk 5.0 新增的功能 Annotation 其实就是代码里的特殊标记, 这些标记可以在编译, 类加载, 运行时被读取, 并执行相应的处理.通 ...

  2. 【刷题-LeetCode】147 Insertion Sort List

    Insertion Sort List Sort a linked list using insertion sort. A graphical example of insertion sort. ...

  3. 【代码分享】用redis+lua实现多个集合取交集并过滤,类似于: select key from set2 where key in (select key from set1) and value>=xxx

    redis中的zset结构可以看成一个个包含数值的集合,或者认为是一个关系数据库中用列存储方式存储的一列. 需求 假设我有这样一个数据筛选需求,用SQL表示为: select key from set ...

  4. vue学习15-自定义组件model使用

    <!DOCTYPE html> <html lang='en'> <head> <meta charset='UTF-8'> <meta http ...

  5. 小程序或者vue,解决菜单导航做做成轮播的样子

    案例: 其中最重要的思路就是如何让第二次或第三次以及后面的轮播有数据: 做法大致跟轮播图做法一样,只不过我们需要进行书写样式,代码如下: <!-- 做一个轮播图navbar demo --> ...

  6. 外观模式(Facade模式)

    外观模式的定义与特点 外观(Facade)模式又叫作门面模式,是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式.该模式对外有一个统一接口,外部应用程序不用关心内部子系 ...

  7. C#8.0 可空引用类型

    介绍 我们的项目代码运行时最频繁的错误之一就是 System.NullReferenceException 异常,c#8.0增加的可为空引用类型就是用来帮助开发者降低甚至消除NULL异常.我们需要注意 ...

  8. gin中HTML渲染

    package main import ( "github.com/gin-gonic/gin" "net/http" ) func login(ctx *gi ...

  9. Hexo博客(Snail主题)搭建回顾概览

    Hexo博客(Snail主题)搭建回顾概览 笔者搭建博客地址:https://saltyfishyjk.github.io 目录 Hexo博客(Snail主题)搭建回顾概览 Part 0 前言 写作背 ...

  10. tcp 中 FLAGS字段,几个标识:SYN, FIN, ACK, PSH, RST, URG.

    在TCP层,有个FLAGS字段,这个字段有以下几个标识:SYN, FIN, ACK, PSH, RST, URG. 其中,对于我们日常的分析有用的就是前面的五个字段.它们的含义是: 1.SYN表示建立 ...