Python之面向对象

Posted

tags:

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

一.面向对象的设计意义

面向过程的程序设计的核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西。

优点是:极大的降低了程序的复杂度;

缺点是:可扩展性差,修改代码麻烦;

应用场景:一旦完成基本很少改变的场景,著名的例子有Linux內核,git,以及Apache HTTP Server等。

 

面向对象的程序设计的核心是对象(上帝式思维),要理解对象为何物,必须把自己当成上帝,上帝眼里世间存在的万物皆为对象,不存在的也可以创造出来。面向对象的程序设计好比如来设计西游记,如来要解决的问题是把经书传给东土大唐,如来想了想解决这个问题需要四个人:唐僧,沙和尚,猪八戒,孙悟空,每个人都有各自的特征和技能(这就是对象的概念,特征和技能分别对应对象的数据属性和方法属性),然而这并不好玩,于是如来又安排了一群妖魔鬼怪,为了防止师徒四人在取经路上被搞死,又安排了一群神仙保驾护航,这些都是对象。然后取经开始,师徒四人与妖魔鬼怪神仙交互着直到最后取得真经。如来根本不会管师徒四人按照什么流程去取。

 

面向对象的程序设计的

优点:解决了程序的扩展性。对某一个对象单独修改,会立刻反映到整个体系中,如对游戏中一个人物参数的特征和技能修改都很容易。

缺点:可控性差,无法向面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题,无法预测最终结果。于是我们经常看到一个游戏人某一参数的修改极有可能导致bug的技能出现,一刀砍死3个人,这个游戏就失去平衡。

应用场景:需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方。

面向对象的程序设计并不是全部。对于一个软件质量来说,面向对象的程序设计只是用来解决扩展性。

二.类和对象

1.定义

python中一切皆为对象,且python3统一了类与类型的概念,类型就是类。

基于面向对象设计一个款游戏:英雄联盟,每个玩家选一个英雄,每个英雄都有自己的特征和和技能,特征即数据属性,技能即方法属性,特征与技能的结合体就一个对象。

从一组对象中提取相似的部分就是类,类所有对象都具有的特征和技能的结合体。

在python中,用变量表示特征,用函数表示技能,因而类是变量与函数的结合体,对象是变量与方法(指向类的函数)的结合体。

2.类的声明

在python中声明类与声明函数很相似

  • 声明类
‘‘‘
class 类名:
    ‘类的文档字符串‘
    类体
‘‘‘

#我们创建一个类
class Data:
    pass
  •  分类:新式类和经典类

1.只有在python2中才分新式类和经典类,python3中统一都是新式类
2.新式类和经典类声明的最大不同在于,所有新式类必须继承至少一个父类
3.所有类甭管是否显式声明父类,都有一个默认继承object父类(讲继承时会讲,先记住)

在python2中的区分
经典类:
class 类名:
  pass

新式类:
class 类名(父类):
  pass

在python3中,上述两种定义方式全都是新式类

例:

class Garen:        #定义英雄盖伦的类,不同的玩家可以用它实例出自己英雄;
    camp=‘Demacia‘  #所有玩家的英雄(盖伦)的阵营都是Demacia;
    def attack(self,enemy):   #普通攻击技能,enemy是敌人;
        enemy.life_value-=self.aggressivity #根据自己的攻击力,攻击敌人就减掉敌人的生命值。

 3.类的两种用法

  • 类属性的引用
class Garen:        #定义英雄盖伦的类,不同的玩家可以用它实例出自己英雄;
    camp=‘Demacia‘  #所有玩家的英雄(盖伦)的阵营都是Demacia;
    def attack(self,enemy):   #普通攻击技能,enemy是敌人;
        enemy.life_value-=self.aggressivity #根据自己的攻击力,攻击敌人就减掉敌人的生命值。

print(Garen.camp) #引用类的数据属性,该属性与所有对象/实例共享

print(Garen.attack) #引用类的函数属性,该属性也共享

print(Garen.__dict__) #查看类的属性字典,或者说名称空间

# Garen.name=‘Garen‘)#增加属性
# del Garen.name #删除属性

"""
输出:
Demacia <function Garen.attack at 0x106acd400> {__doc__‘: None, attack‘: <function Garen.attack at 0x106acd400>, __module__‘: __main__‘, __weakref__‘: <attribute __weakref__‘ of Garen‘ objects>, camp‘: Demacia‘, __dict__‘: <attribute __dict__‘ of Garen‘ objects>}

"""
  •  类的实例化

类名加括号就是实例化,会自动触发__init__函数的运行,可以用它来为每个实例定制自己的特征

class Garen:        #定义英雄盖伦的类,不同的玩家可以用它实例出自己英雄;
    camp=‘Demacia‘  #所有玩家的英雄(盖伦)的阵营都是Demacia;
    def __init__(self,nickname,aggressivity=58,life_value=455): #英雄的初始攻击力58...;
        self.nickname=nickname  #为自己的盖伦起个别名;
        self.aggressivity=aggressivity #英雄都有自己的攻击力;
        self.life_value=life_value #英雄都有自己的生命值;
    def attack(self,enemy):   #普通攻击技能,enemy是敌人;
        enemy.life_value-=self.aggressivity #根据自己的攻击力,攻击敌人就减掉敌人的生命值。

g1=Garen(‘草丛伦‘) #就是在执行Garen.__init__(g1,‘草丛伦‘),然后执行__init__内的代码g1.nickname=‘草丛伦’等
print(g1)
print(g1.nickname)
"""
输出:
<__main__.Garen object at 0x10ee26e10>
草丛伦
"""

注: self的作用是在实例化时自动将对象/实例本身传给__init__的第一个参数,self可以是任意名字

补充:

一:我们定义的类的属性到底存到哪里了?有两种方式查看
1.dir(类名):查出的是一个名字列表
2.类名.__dict__:查出的是一个字典,key为属性名,value为属性值

二:特殊的类属性
类名.__name__# 类的名字(字符串)
类名.__doc__# 类的文档字符串
类名.__base__# 类的第一个父类(在讲继承时会讲)
类名.__bases__# 类所有父类构成的元组(在讲继承时会讲)
类名.__dict__# 类的字典属性
类名.__module__# 类定义所在的模块
类名.__class__# 实例对应的类(仅新式类中)

 4.对象的产生和引用

  • 对象是关于类而实际存在的一个例子,即类的实例化。
g1=Garen(‘草丛伦‘) #实例化类就是对象
print(g1)
print(g1.nickname)
print(type(g1)) #查看g1的类型就是类Garen
print(isinstance(g1,Garen)) #g1就是Garen的实例
"""
<__main__.Garen object at 0x108d55dd8>
草丛伦
<class ‘__main__.Garen‘>
True
"""
  • 对象的引用只有一种:属性引用

对象/实例本身其实只有数据属性

class Garen:        #定义英雄盖伦的类,不同的玩家可以用它实例出自己英雄;
    camp=‘Demacia‘  #所有玩家的英雄(盖伦)的阵营都是Demacia;
    def __init__(self,nickname,aggressivity=58,life_value=455): #英雄的初始攻击力58...;
        self.nickname=nickname  #为自己的盖伦起个别名;
        self.aggressivity=aggressivity #英雄都有自己的攻击力;
        self.life_value=life_value #英雄都有自己的生命值;
    def attack(self,enemy):   #普通攻击技能,enemy是敌人;
        enemy.life_value-=self.aggressivity #根据自己的攻击力,攻击敌人就减掉敌人的生命值。

g1=Garen(‘草丛伦‘) #类的实例化就是对象
print(g1.nickname)  #对象的属性引用
print(g1.aggressivity)
print(g1.life_value)
"""
草丛伦
58
455
"""

补充:

对象/实例本身只有数据属性,但是python的class机制会将类的函数绑定到对象上,称为对象的方法,或者叫绑定方法,绑定方法唯一绑定一个对象,同一个类的方法绑定到不同的对象上,属于不同的方法,内存地址都不会一样。

print(g1.attack)  #对象的绑定方法
print(Garen.attack)  #对象的绑定方法attack本质就是调用类的函数attack的功能,二者是一种绑定关系
"""
输出: <bound method Garen.attack of <__main__.Garen object at 0x10809ee10>> <function Garen.attack at 0x1081e3488> """

 对象的绑定方法的特别之处在于:obj.func()会把obj传给func的第一个参数。

5.对象之间的交互

仿照garen类再创建一个Riven类,用瑞雯攻击盖伦,完成对象交互

class Garen:        #定义英雄盖伦的类,不同的玩家可以用它实例出自己英雄;
    camp=‘Demacia‘  #所有玩家的英雄(盖伦)的阵营都是Demacia;
    def __init__(self,nickname,aggressivity=58,life_value=455): #英雄的初始攻击力58...;
        self.nickname=nickname  #为自己的盖伦起个别名;
        self.aggressivity=aggressivity #英雄都有自己的攻击力;
        self.life_value=life_value #英雄都有自己的生命值;
    def attack(self,enemy):   #普通攻击技能,enemy是敌人;
        enemy.life_value-=self.aggressivity #根据自己的攻击力,攻击敌人就用敌人的生命值减攻击力。


class Riven:
    camp=‘Noxus‘  #所有玩家的英雄(锐雯)的阵营都是Noxus;
    def __init__(self,nickname,aggressivity=54,life_value=414): #英雄的初始攻击力54;
        self.nickname=nickname  #为自己的锐雯起个别名;
        self.aggressivity=aggressivity #英雄都有自己的攻击力;
        self.life_value=life_value #英雄都有自己的生命值;
    def attack(self,enemy):   #普通攻击技能,enemy是敌人;
        enemy.life_value-=self.aggressivity #根据自己的攻击力,攻击敌人就用敌人的生命值减攻击力。


g1=Garen(‘盖伦‘) #就是在执行Garen.__init__(g1,‘草丛伦‘),然后执行__init__内的代码g1.nickname=‘草丛伦’等
r1=Riven(‘瑞雯‘)

print(g1.life_value)
print(r1.attack(g1))    #对象交互,瑞雯攻击盖伦
print(g1.life_value)
"""
输出:
455
None
401
"""

 6.类名称空间与对象/实例名称空间

创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性。

类有两种属性:数据属性和函数属性

其中类的数据属性是共享给所有对象的

定义在类内部的变量,是所有对象共有的,id全一样

print(id(r1.camp)) #本质就是在引用类的camp属性,二者id一样
print(id(Riven.camp))
"""
输出: 4482776512 4482776512 """

而类的函数属性是绑定到所有对象的:

定义在类内部的函数,是绑定到所有对象的,是给对象来用,obj.func() 会把obj本身当做第一个参数出入

r1.attack就是在执行Riven.attack的功能,python的class机制会将Riven的函数属性attack绑定给r1,r1相当于拿到了一个指针,指向Riven类的attack功能,r1.attack()会将r1传给attack的第一个参数

print(id(r1.attack))
print(id(Riven.attack))
"""
输出:
4372850184
4374779288
"""

注:

创建一个对象/实例就会创建一个对象/实例的名称空间,存放对象/实例的名字,称为对象/实例的属性

在obj.name会先从obj自己的名称空间里找name,找不到则去类中找,类也找不到就找父类...最后都找不到就抛出异常  

print(g1.x) #先从g1.__dict__,找不到再找Garen.__dict__,找不到就会报错

四.继承和派生

1.继承的意义

继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类。

  • 单继承和多继承
class ParentClass1: #定义父类
    pass

class ParentClass2: #定义父类
    pass

class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass
    pass

class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类
    pass
  •  查看继承

 __base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类

print(SubClass2.__base__)
print(SubClass2.__bases__)
"""
输出:
<class ‘__main__.ParentClass1‘>
(<class ‘__main__.ParentClass1‘>, <class ‘__main__.ParentClass2‘>)
"""

 如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现。

print(ParentClass1.__base__)
"""
输出:
<class ‘object‘>
"""

 

 

 

 

 

 

 

7.

 





以上是关于Python之面向对象的主要内容,如果未能解决你的问题,请参考以下文章

VSCode自定义代码片段——JS中的面向对象编程

VSCode自定义代码片段9——JS中的面向对象编程

PHP面向对象之选择工厂和更新工厂

python基础之面向对象

Python之面向对象:面向对象基础

python之面向对象编程一