Python的核心知识——面向对象

一、WHY 学习面向对象?

假设现在有一个场景是这样的,我们要编写一个游戏,里面有主角,NPC,怪物等等这些模型,那么如果我们不学面向对象的话,我们就需要一个一个的编写它们的属性及动作,一般情况下,一个大型的游戏比如LOL(这游戏是真的火,都好几年了还没被干掉)里面的英雄,小兵,大龙,有好上百个模型吧。我们不可能一个一个的写,那代码量就不是一两个g那么大了(其实这就是面向过程的思想,POP)!仔细观察会发现它们这些模型都有相同的属性和动作,比如:都有生命值,魔法值等属性,攻击等动作,所以编程语言开发者们就在想了,有没有一个东西把它们共同的属性、动作封装起来,这时候类概念应运而生了,就是我们这篇文章的主体对象,而这种思想就是面向对象(OOP)!

那什么是面向对象编程呢?

度娘一下:面向对象(Object Oriented)是软件开发方法。面向对象的概念和应用已超越了程序设计和软件开发,扩展到如数据库系统、交互式界面、应用结构、应用平台、分布式系统、网络管理结构、CAD技术、人工智能等领域。面向对象是一种对现实世界理解和抽象的方法,是计算机编程技术发展到一定阶段后的产物。

巴拉巴拉一堆,感觉懵懵懂懂,个人的理解就是,一种思想,相对于面向过程的一种思想。这是我个人对关于面向对象与面向过程的区别的理解:链接在此

二、类和对象

类和对象是面向对象编程里面的两个非常重要的概念。它们的关系嘛,嗯~,如果是说类是一张汽车的图纸,那么一辆根据这个图纸制造出来的汽车就是一个对象!

1.类

类啥是类?用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。就可以理解为之前我们学习的函数,是一种有自治系统的函数,有自己的属性方法等等。类也是虚的,和函数一样,只有当它被实例化时才是计算机要运行的代码块,一个稿纸。

2.对象

对象又是啥?女朋友?男朋友?nonono,编程中的对象可不男女朋友万能多了,它可以帮你制造一个男朋友或者女朋友,厉害吧!那啥是对象?对象就是类的实例化,上面说了类是虚的,纸上谈兵,而对象是实,招兵买马。和函数一样,函数是虚的,调用函数是实的。

三、定义类和创建对象

那问题来了,知道它们的重要性,就得学会用它们了吧!那怎么用?听我慢慢道来!

首先这里推荐一个适合工作用的Python编程工具(看个人吧!想用就用,毕竟好东西要一起分享嘛!):pycharm

注意:像这种官方工具,尽量使用专业版,首先同为在IT行业的我们应该尊重编写pycharm的团队成果,其次在使用社区版的软件是不全面,这时候我们就会想用盗版破解软件,如果使用盗版软件,在以后的工作中就会涉及到版权的法律问题,那就不是一两百可以解决的问题了。

1.类的定义

格式:

class 类的名字:
…类的属性 = 属性值
…类的方法(self, [参数]):
… …方法体

类的属性,在Python中类的属性的定义与变量定义一样,记得赋值
类的方法,在Python中类的方法的定义与普通的函数定义方法一样,记得加self变量
代码

class Str:
    #一般Python属性的定义是直接赋值的,不像C++等编程语言是不可以赋值的
    name = "cc"
    age = 18
    #类的方法定义在参数表里,必须加上一个变量,一般情况Python编程者普遍使用self为变量名
    #self就是相对于C++的this指针,由于类是模型,而对象是由类实例化产生的,所以为了区分不同的对象,我们通过引进self变量区别
    #self变量的意思是将实例化后的对象的名字传递给self,而这种传递是在类里面的,调用方法时不需要传递
    #说白了,self就是一种标记实例化对象的全局变量,而方法里面的变量是局部变量
    #__init__魔法方法就是对类的初始化,在定义时本身就有的,现在是把它进行覆盖(后面会写),是不需要在实例化时调用的
    #通常称__init__为构造方法
    #其实类里面还有很多类似的方法,自行探索
    #self.name是对类内部属性的访问或者复制
    def __init__(self, Name):
    	self.name = Name
    def S_print(self):
        print("Name is {}, Age is {}".format(self.name, self.age))

类定义是虚的,不会在程序中显示!

2.对象的创建

格式:

实例化对象名 = 类名([参数])
实例化对象名.类属性名
实例化对象名.类方法名

代码

class Str:
    name = "cc"
    age = 18
    def __init__(self, Name):
    	self.name = Name
    def S_print(self):
        print("Name is {}, Age is {}".format(self.name, self.age))

s = Str("qq")	#括号里的参数是当__init__魔法方法的列表里面有除了self属性外,还有其他属性的话,才添加的
print(s.name)
s.S_print()

结果显示
在这里插入图片描述

四、面向对象三大特征

Python之所以号称面向对象编程语言,是因为具有面向对象编程特有的三个特征:封装、继承、多态。

类对象正是因为有了这三大法宝才变得如此这般之厉害!

1.封装

封装顾名思义就是打包的意思,就是将类里面的属性和方法打包起来,放在类里面成为一个整体,这样属性和方法可以私有化了,并且提供了公共的方法去访问和赋值类的属性。

通过对类属性的封装使得类属性更加稳定和安全,同时通过属性私有化,还必须提供setter(设置方法)和getter(获取方法) 两种方法来让外界访问和修改属性。

在Python中想要私有化类中的属性和方法只要在属性和方法前面添加两个下划线__就可以了(例如:__name),而单下划线_也是一种私有的(例如:_name),但是这种下划线属于口头私有,即意思是虽然可以被访问,但是请把其视为私有变量,不要随意访问(好坑)。

在Python中可以通过对象名.类属性名/方法名进行访问属性和调用方法。

在Python中的私有属性和方法是伪私有,就是有它们只换了一个名,在外部也可以进行访问,只要在私有属性前面加上单引号类名_类名__属性名即可(例如:对象名._类名__属性名,由于是访问,所以加对象名)

Python有三种封装的写法

(1)直接私有化属性,提供getter和setter
(2)直接私有化属性,提供getter和setter,引入了property的全局函数
(3)使用property装饰器完成封装

a.直接私有化属性,提供getter和setter

这种方法就是提供gettersetter两种方法,比较常用的方法了。

格式

def 获取属性方法名(self):
…return self.__属性名
def 设置属性方法名(self, 用户输入的值):
self.__属性名 = 用户输入的值

代码

class Str:
    __name = "cc"
    _age = 18
    def __init__(self, Name):
    	self.__name = Name
    #提供name_getter方法来返回name值
    def name_getter(self):
    	return self.__name
    #提供name_setter方法来设置name值
    def name_setter(self, Name):
    	self.__name = Name
    def S_print(self):
        print("Name is {}, Age is {}".format(self.__name, self._age))
s = Str("qq")
print(s.name_getter())
s.name_setter("xx")
print(s.name_getter())
print(s._age)
print(s.__name)

结果显示
在这里插入图片描述

由结果可以看出我们在__init__(构造方法)前面定义属性是没必要的,一般情况下,会在构造方法里面定义属性,并赋初值。

有两个下划线的属性是不可以访问的,有单下划线的属性可以访问。

b.直接私有化属性,提供getter和setter,引入了property的全局函数

这种方法是在上面的方法基础上引入property全局函数,使得在访问属性时更加方便。

函数格式

class property([fget[, fset[, fdel[, doc]]]])

参数

fget – 获取属性值的函数
fset – 设置属性值的函数
fdel – 删除属性值函数
doc – 属性描述信息

方法格式

def 获取属性方法名(self):
…return self.__属性名
def 设置属性方法名(self, 用户输入的值):
self.__属性名 = 用户输入的值
变量名(推荐属性名,就相当于设置了一个属性)=property(设置属性方法名, 获取属性方法名)

注意:设置属性方法名在前面,获取方法名在后面

代码

class Str:
    def __init__(self, Name):
    	self.__name = Name
    #提供name_getter方法来返回name值
    def name_getter(self):
    	return self.__name
    #提供name_setter方法来设置name值
    def name_setter(self, Name):
    	self.__name = Name

    name = property(name_getter, name_setter)    
    def S_print(self):
        print("Name is {}, Age is {}".format(self.__name, self._age))
s = Str("qq")
print(s.name)
s.name = "xx"
print(s.name)

结果显示
在这里插入图片描述

c.使用property装饰器完成封装

上面的调用方法又略显复杂,这对于追求完美的Python程序员来说,是不太满意的!所以就出现了第三种方法。这种方法是通过@property装饰器负责把一个方法变成只读属性调用。

把一个getter方法变成属性,只需要加上@property就可以了,此时,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值,于是,我们就拥有一个可控的属性操作。

@property广泛应用在类的定义中,可以让调用者写出简短的代码,同时保证对参数进行必要的检查,这样,程序运行时就减少了出错的可能性。

格式

@property
def 属性方法名(self):
return self.__属性名
@属性方法名.setter
def 属性方法名(self, 用户输入的值):
self.__属性名 = 用户输入的值

代码

class Str:
    def __init__(self, Name):
    	self.__name = Name
        
    @property
    def S_name(self):
        return self.__name

    @S_name.setter
    def S_name(self, Name):
        self.__name = Name

   
    def S_print(self):
        print("Name is {}, Age is {}".format(self.__name, self._age))
s = Str("qq")
s.name = "xx"
print(s.name)

结果显示
在这里插入图片描述
注意:在使用第三种封装方法时@方法名.setter中必须是setter,而且由于是配对的,所以只有先设置才可以调用。

2.继承

继承嘛!就是继承父辈的遗产或者知识类似,模拟人类的继承这种方式,子代继承父代的财富或知识。由于面向对象是模拟人类的实现,所以需要有继承关系,即类与类之间的继承关系。其实在Python3.x版本中所有的自定义类都继承object类,但是Python2.x中却不是,Python和C++一样还支持多继承。

当一个类继承其他类(也就是父类,也可以叫超类),那么它就拥有了继承父类的一些方法和属性。如果我们继承了父类的方法和属性,如果这些方法和属性满足我们使用,直接调用就行了。但是如果某个方法或者属性不满足我们的需求可以通过重写(覆盖)代码。

注意:父类的公开方法和属性可以继承,私有方法和属性是不能被继承,但是嘛!上面说了Python是伪私有,所以还是可以通过非正常手段继承父类私有方法的。

super继承两种方法,父类有属性加变量名

a.正常继承

格式

class 子类(父类):
…类的属性 = 属性值
…类的方法(self, [参数]):
… …方法体
#上面那块代码块和之前定义类的形式一样了,就是看你继不继承父类属性和方法了

代码

#父类
class People:
    def __init__(self):
        print("我是父类")
#子类1
class Employee(People):
    pass
#子类2
class Teacher(People):
    def __init__(self):
        print("我是子类2")

P =  People()
E =  Employee()
T = Teacher()

显示结果
在这里插入图片描述
由结果可以看出一个父类可以有多个子类,就像一个父亲可以有多个孩子一样!

b.非法继承

一种方法是通过父类名.方法名(self),这种方法是非绑定,另一种是调用super函数。这种方法是绑定。
格式

父类名.方法名(self)
super().init()

代码

#父类
class People:
    def __init__(self):
        self.__name = "xx"
        print("我是父类")
    def P_print(self):
        print("我是{}".format(self.__name))
#子类1
class Employee(People):
    def __init__(self):
        People.__init__(self)
        print("我是子类1\n")

#子类2
class Teacher(People):
    def __init__(self):
        super().__init__()
        print("我是子类2\n")

P =  People()
E =  Employee()
T = Teacher()
P.P_print()
E.P_print()
T.P_print()

显示结果
在这里插入图片描述

c.多继承

多继承的意思就是一个徒弟有多个师傅传授武功。Python中也一样一个子类可以有多个父类,但是不推荐,这种方法很容易使得代码显得复杂,而且容易抛出异常。

格式

class 子类名(父类1, 父类2,…):

代码

#父类1
class People:
    def P_print(self):
        print("我是父类1")
#父类2
class Employee:
    def E_print(self):
        print("我是父类2")

#子类
class Teacher(People, Employee):
    pass
P =  People()
E =  Employee()
T = Teacher()
P.P_print()
E.E_print()
T.P_print()
T.E_print()

结果显示
在这里插入图片描述

d.Python没有函数的重载,但是有重写(或者叫覆盖)

重载就是函数名称相同,函数参数个数、参数类型和返回值可以不同的两个或者两个以上的函数,当我们调用的时候,会自动的执行对应的函数。

而重写就是函数名称,函数参数个数、参数类型和返回值都相同的两个函数或者以上的函数。

由于Python语言本身没有数据类型这一说,加上是脚本语言,变量会随着赋值的改变而改变,所以Python是没有函数重载的,但是Python是支持重写的。

重写格式

def 父类的方法([参数]):
…方法体
def 与父类相同名字的子类方法([相同参数]):
…不同方法体

代码

#父类1
class People:
    def P_print(self):
        print("我是父类")

#子类
class Teacher(People):
    def P_print(self):
        print("我是子类")

P =  People()
T = Teacher()
P.P_print()
T.P_print()

结果显示
在这里插入图片描述

3.多态

多态是在继承的基础上,父类引用指向子类实例(对下个)的现象,就是说父类可以调用子类方法!!!

在Python这些弱类型语音,天生支持多态。

为什么呢?

因为Python是在定义变量、方法名或者类的时候是没有数据类型怎么一说的。首先我们看看C++的多态,假设AList继承List这个父类,那么我们就可以定义:

List a = new AList()

而在Python中是没有数据类型在变量前面修饰的,就像下面:

a = AList()

所以在Python这些弱类型语音,天生支持多态。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章