Python 面向对象程序设计

作者:billy
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处

什么是面向对象

面向对象(Object Oriented)的英文缩写是 OO,它是一种设计思想。从 20 世纪 60 年代提出面向对象的概念到现在,它已经发展成为一种比较成熟的编程思想,并且逐步成为目前软件开发领域的主流技术。如我们经常听说的面向对象编程(Object Oriented Programming,即 OOP)就是主要针对大型软件设计而提出的。他可以使软件设计更加灵活,并且能更好地进行代码复用

面向对象中的对象(Object),通常是指客观世界中存在的对象,这个对象具有唯一性,对象之间各不相同,各有各的特点,每个对象都有自己的运动规律和内部状态;对象和对象之间又是可以相互联系、相互作用的。另外,对象也可以是一个抽象的事物。例如,可以从圆形、正方形、三角形等图形中抽象出一个简单图形,简单图形就是一个对象,它有自己的属性和行为,图形中边的个数是它的属性,图形的面积也是它的属性,输出图形的面积就是它的行为。概括地讲,面向对象技术是一种从组织结构上模拟客观世界的方法。

  • 对象
    对象,是一个抽象概念,英文称作 “Object”,表示任意存在的事物(例如,一个人就是一个对象)。通常将对象划分为两个部分,即静态部分和动态部分。静态部分称为 “属性”(例如,人的性别),而动态部分称为 “行为”(例如,人走路)。

在 Python 中,一切都是对象。即不仅是具体事物称为对象,字符串、函数等也都是对象。这说明 Python 天生就是面向对象的


  • 类是封装对象的属性和行为的载体,反过来说,具有相同属性和行为的一类实体被称为类。在 Python 语言中,类是一种抽象概念,如定义一个大雁类(Geese),在该类中,可以定义每个对象共有的属性和方法;而一只要从北方飞往南方的大雁则是大雁类的一个对象(wildGeese),对象是类的实例。

  • 面向对象程序设计的特点
    面向对象程序设计具有三大基本特征:封装、继承、多态

  1. 封装
    封装是面向对象编程的核心思想,将对象的属性和行为封装起来,而将对象的属性和行为封装起来的载体就是类,类通常对客户隐藏其实现细节,就这事封装的思想
    采用封装思想保证了类内部数据结构的完整性,使用该类的用户不能直接看到类中的数据结构,而只能执行类允许公开的数据,这样就避免了外部对内部数据的影响,提高了程序的可维护性。

  2. 继承
    继承是实现重复利用的重要手段,子类通过继承复用了父类的属性和行为的同时,又添加了子类特有的属性和行为。

  3. 多态
    将父类对象应用于子类的特征就是多态

类的定义和使用

在 Python 中,使用 class 关键字来定义一个类,语法如下:

class ClassName:
    "类的帮助信息"		# 类文档字符串
    statement			# 类体

ClassName:用于指定类名,一般使用大写字母开头,如果类名中包括两个单词,第二个单词的首字母也大写,这种命名方式也称为 “驼峰命名法”,这是惯例。当然,也可根据自己的习惯命名,但是一般推荐按照惯例来命名;
“类的帮助信息”:用于指定类的文档字符串,定义该字符串后,在创建类的对象时,输入类名和左侧的括号 ‘(’ 后,将显示该信息;
statement:类体,主要由类变量(或类成员)、方法和属性等定义语句组成。如果在定义类时,没想好类的具体功能,也可以在类体中直接使用 pass 语句代替;

class 语句本身并不创建该类的任何实例。所以在类定义完成之后,需要创建类的实例,即实例化该类的对象

在创建类后,通常会创建一个 __init__() 方法,该方法是一个特殊的方法,类似 Java 语言中的构造方法。每当创建一个类的新实例时,Python 都会自动执行它。__init__() 方法必须包含一个 self 参数,并且必须是第一个参数。self 参数是一个指向实例本身的引用,用于访问类中的属性和方法,在方法调用时会自动传递实际参数 self。因此,当 __init__() 方法只有一个参数时,在创建类的实例时,就不需要指定实际参数了。

示例:

class Geese:
    "大雁类"
    def __init__(self, beak, wing, claw):       # 构造方法
        print("我是大雁类!我有以下特征:")
        print(beak)                             # 输出喙的特征
        print(wing)                             # 输出翅膀的特征
        print(claw)                             # 输出爪子的特征

beak = "喙的基部较高,长度和头部的长度几乎相等"	# 喙的特征
wing = "翅膀长而尖"                              # 翅膀的特征
claw = "爪子是噗状的"                            	# 爪子的特征
wildGeese = Geese(beak, wing, claw)             # 创建大雁类的实例

上述例子的运行结果为:

我是大雁类!我有以下特征:
喙的基部较高,长度和头部的长度几乎相等
翅膀长而尖
爪子是噗状的

创建类的成员并访问

类的成员主要由实例方法和数据成员组成。

所谓实例方法,是指在类中定义的函数。该函数是一种在类的实例上操作的函数。同 __init__() 方法一样,实例方法的第一个参数必须是 self,并且必须包含一个 self 参数

而数据成员,是指在类中定义的变量,即属性。根据定义位置,又可以分为类属性和实例属性。类属性是指定义在类中,并且在函数体外的属性,类属性可以在类的所有实例之间共享值,也就是在所有实例化的对象中公用;实例属性是指定义在类的方法中的属性,只作用于当前实例中

示例:

class Geese:
    "大雁类"
    neck = "脖子较长"                           	# 类属性 - 脖子
    wing = "振翅频率高"                        	# 类属性 - 翅膀
    leg = "腿位于身体的中心支点,行走自如"        	# 类属性 - 腿
    number = 0                                  # 类属性 - 编号

    def __init__(self):                       	# 构造方法
        Geese.number += 1                    	# 将编号 + 1
        print("我是第 " + str(Geese.number) + " 只大雁,我属于雁类!")

    def fly(self, state):                    	# 定义飞行方法
        self.myNeck = "脖子没有天鹅的长"		# 实例属性 - 我的脖子
        
        print("我有以下特征:")
        print(self.myNeck)                      # 输出我的脖子的特征
        print(self.neck)                        # 输出脖子的特征
        print(self.wing)                        # 输出翅膀的特征
        print(self.leg)                         # 输出爪子的特征
        print(state)                            # 输出飞行特征
        print()

list = []

wildGeese1 = Geese()				# 创建大雁类的实例
wildGeese1.fly("飞行的时候,会排成人字!")		# 调用实例方法
list.append(wildGeese1)

wildGeese2 = Geese()				# 创建大雁类的实例
wildGeese2.neck = "脖子较短"			# 通过实例来修改类属性
wildGeese2.wing = "翅膀是黑色的"
wildGeese2.leg = "是个小短腿"
wildGeese2.fly("飞行的时候,会排成一字!")		# 调用实例方法
list.append(wildGeese2)

wildGeese3 = Geese()				# 创建大雁类的实例
Geese.neck = "脖子不长不短"			# 通过类名来修改类属性
Geese.wing = "翅膀上羽毛很多"
Geese.leg = "是个大长腿"
wildGeese3.fly("飞行的时候,会排成八字!")		# 调用实例方法
list.append(wildGeese3)

list[0].myNeck = "我脖子的长度超过了天鹅的脖子"
print("第 1 只大雁的 myNeck 属性:", list[0].myNeck)	# 实例属性,修改之后只有本实例自己有效,其他实例无变化
print("第 2 只大雁的 myNeck 属性:", list[1].myNeck)
print()

Geese.beak = "喙的基部较高,长度和头部的长度几乎相等"	# 动态地为类和对象添加类属性,所有实例都会改变
print("第 2 只大雁的喙:", list[1].beak)

上述例子的运行结果为:

我是第 1 只大雁,我属于雁类!
我有以下特征:
脖子没有天鹅的长
脖子较长
振翅频率高
腿位于身体的中心支点,行走自如
飞行的时候,会排成人字!

我是第 2 只大雁,我属于雁类!
我有以下特征:
脖子没有天鹅的长
脖子较短
翅膀是黑色的
是个小短腿
飞行的时候,会排成一字!

我是第 3 只大雁,我属于雁类!
我有以下特征:
脖子没有天鹅的长
脖子不长不短
翅膀上羽毛很多
是个大长腿
飞行的时候,会排成八字!

第 1 只大雁的 myNeck 属性: 我脖子的长度超过了天鹅的脖子
第 2 只大雁的 myNeck 属性: 脖子没有天鹅的长

第 2 只大雁的喙: 喙的基部较高,长度和头部的长度几乎相等

访问限制

在 Python 中没有对属性和方法的访问权限进行限制。为了保证类内部的某些属性或方法不被外部访问,可以在属性或方法名前面添加单下划线(_foo)、双下划线(__foo)或首位加双下划线(__foo__),从而限制其权限,作用如下表所示:

类型 功能
_foo 以单下划线开头表示 protested(保护)类型的成员,只允许类本身和子类进行访问,但不能使用 “from module import *” 语句导入
__foo 双下划线表示 private(私有)类型的成员,只允许定义该方法的类本身进行访问,而且不能通过类的实例进行访问,但是可以通过 “类的实例名._类名__xxx” 方式访问
__foo__ 首尾双下划线表示定义特殊方法,一般是系统定义名字,如 __init__()

示例:

class Swan:
    "天鹅类"
    _neck = "天鹅的脖子很长"				# 定义保护属性
    __feather = "天鹅的羽毛很美"                       	# 定义私有属性

    def __init__(self):
        print("__init__(): ", Swan._neck)           	# 在实例方法中访问保护属性
        print("__init__(): ", Swan.__feather)       	# 在实例方法中访问私有属性

swan = Swan()                                       	# 创建实例对象
print("直接访问保护属性: ", swan._neck)                	# 保护属性可以通过实例名访问
print("加入类名来访问私有属性: ", swan._Swan__feather)   	# 私有属性,可以通过 "实例名._类名__xxx" 方式访问
print("直接访问私有属性: ", swan.__feather)             	# 私有属性不能通过实例名访问,出错

上述例子的运行结果为:

__init__():  天鹅的脖子很长
__init__():  天鹅的羽毛很美
直接访问保护属性:  天鹅的脖子很长
加入类名来访问私有属性:  天鹅的羽毛很美
Error

用于计算的属性

在 Python 中,可以通过 @property(装饰器)将一个方法转换为属性,从而实现用于计算的属性,将方法转换为属性后,可以直接通过方法名来访问方法,而不需要再添加一对小括号 “()”,这样使得代码更简洁。

示例:

class Rect:
    def __init__(self, width, height):
        self.width = width                  	# 矩形的宽
        self.height = height                	# 矩形的高

    @property                              	# 将方法转换为属性
    def area(self):                         	# 计算矩形的面积的方法
        return self.width * self.height     	# 返回矩形的面积

rect = Rect(800, 600)                       	# 创建实例对象
print("矩形面积为: ", rect.area)			# 输出属性的值

上述例子的运行结果为:矩形面积为: 480000

默认情况下,创建的类属性或者实例是可以在类体外进行修改的,如果想要限制其不能在类体外修改,可以将其设置为私有的,但设置为私有后,在类体外也不能获取它的值。如果想要创建一个可以读取但不能修改的属性,可以使用 @property 属性

示例:

class TVshow:
    list = ["战狼2", "红海行动", "湄公河行动", "功夫", "魔童降世之哪吒"]

    def __init__(self, show):
        self.__show = show

    @property
    def show(self):
        return self.__show

    @show.setter							# 设置 setter 方法,让属性可修改
    def show(self, value):
        if value in TVshow.list:
            self.__show = "您选择了 《" + value + "》,稍后将播放"		# 返回修改的值
        else:
            self.__show = "您点播的电影不存在"

tvshow = TVshow("战狼2")
print("正在播放: 《", tvshow.show, "》")
print("您可以从 ", tvshow.list, " 中选择要点播的电影")
tvshow.show = "红海行动"							# 修改属性值
print(tvshow.show)							# 获取属性值
tvshow.show = "捉妖记2"							# 修改属性值
print(tvshow.show)							# 获取属性值

上述例子的运行结果为:

正在播放: 《 战狼2 》
您可以从  ['战狼2', '红海行动', '湄公河行动', '功夫', '魔童降世之哪吒']  中选择要点播的电影
您选择了 《红海行动》,稍后将播放
您点播的电影不存在

继承

在编写类时,并不是每次都要从空白开始。当要编写的类和另一个已经存在的类之间存在一定的继承关系时,就可以通过继承来达到代码重用的目的,提高开发效率。

通过继承不仅可以实现代码的重用,还可以通过继承来理顺类与类之间的关系。在 Python 中,可以在类定义语句中,类名右侧使用一对小括号将要继承的基类名称括起来,从而实现类的继承,具体语法格式如下:

class ClassName(baseClassList):
    "类的帮助信息"		# 类文档字符串
    Statement			# 类体

ClassName:用于指定类名;
baseClassList:用于指定要继承的基类,可以用多个,类名之间用逗号 ‘,’ 分隔。如果不指定,将使用所有 Python 对象的根类 object;
“类的帮助信息”:用于指定类的文档字符串,定义该字符串后,在创建类的对象时,输入类名和左侧的括号 ‘(’ 后,将显示该信息;
Statement:类体,主要由类变量(或类成员)、方法和属性等定义语句组成。如果在定义类时,没想好类的具体功能,也可以在类体中直接使用 pass 语句代替;

示例:

class Fruit:						# 定义水果类(基类)
    color = "绿色"					# 定义类属性
    def harvest(self, color):
        print("水果是: " + color + "的")			# 输出的是形式参数 color
        print("水果已经收获......")
        print("水果原来是: " + Fruit.color + "的\n")	# 输出的是类属性 color

class Apple(Fruit):					# 定义苹果类(派生类)
    color = "红色"
    def __init__(self):
        print("我是苹果")

class Orange(Fruit):					# 定义橘子类(派生类)
    color = "橙色"
    def __init__(self):
        print("我是橙子")
    def harvest(self, color):				# 重写基类中的方法
        print("橙子是: " + color + "的")
        print("橙子已经收获......")
        print("橙子原来是: " + Fruit.color + "的")

apple = Apple()						# 创建实例对象(苹果)
apple.harvest(apple.color)				# 调用基类的 harvest() 方法
orange = Orange()					# 创建实例对象(橘子)
orange.harvest(orange.color)				# 调用派生类的 harvest() 方法

上述例子的运行结果为:

我是苹果
水果是: 红色的
水果已经收获......
水果原来是: 绿色的

我是橙子
橙子是: 橙色的
橙子已经收获......
橙子原来是: 绿色的

在派生类中定义 __init__() 方法时,不会自动调用基类的 __init__() 方法。如果要让派生类调用基类的 __init__() 方法进行必要的初始化时,需要在派生类中使用 super() 函数调用基类的 __init__() 方法

示例:

class Fruit:  						# 定义水果类(基类)
    def __init__(self, color = "绿色"):
        Fruit.color = color				# 定义类属性

    def harvest(self, color):
        print("水果是: " + color + "的")  		# 输出的是形式参数 color
        print("水果已经收获......")
        print("水果原来是: " + Fruit.color + "的\n")  	# 输出的是类属性 color

class Apple(Fruit):  					# 定义苹果类(派生类)
    color = "红色"

    def __init__(self):
        print("我是苹果")
        super().__init__()				# 调用基类的 __init__() 方法

class Sapodilla(Fruit):					# 定义人参果类(派生类)
    def __init__(self, color):
        print("我是人参果")
        super().__init__(color)				# 调用基类的 __init__() 方法

    def harvest(self, color):				# 重写基类中的方法
        print("人参果是:" + color + "的")
        print("人参果已经收获......")
        print("人参果原来是: " + Fruit.color + "的")

apple = Apple()  					# 创建实例对象(苹果)
apple.harvest(apple.color)  				# 调用基类的 harvest() 方法
sapodilla = Sapodilla("白色")  				# 创建实例对象(人参果)
sapodilla.harvest("金黄色带紫色条纹")  			# 调用派生类的 harvest() 方法

上述例子的运行结果为:

我是苹果
水果是: 红色的
水果已经收获......
水果原来是: 绿色的

我是人参果
人参果是:金黄色带紫色条纹的
人参果已经收获......
人参果原来是: 白色的

你可能感兴趣的:(Python,Python,面向对象)