Python面向对象程序设计
什么是面向对象程序设计
读者肯定听过 Python 中“一切皆对象”的说法,但可能并不了解它的具体含义,只是在学习的时候听说 Python 是面向对象的编程语言,本节将向大家详细介绍 Python 面向对象的含义。
面向过程VS面向对象
面向过程程序设计范型的主要特征是,程序由过程定义和过程调用组成(简单的说,过程就是程序执行某项操作的一段代码,函数是最常用的过程)
- 导入各种外部库
- 设计各种全局变量
- 写一个函数完成某个功能
- 写一个函数完成某个功能
- 写一个函数完成某个功能
- …
- 写一个main函数作为程序入口
面向对象的程序的基本元素是对象,其主要结构特点是:第一,程序一般由类的定义和类的使用两部分组成;第二,程序中的一切操作都是通过向对象发送消息来实现的,对象接收到消息后,启动相关方法完成相应的操作。
- 导入各种外部库
- 设计各种全局变量
- 决定你要的类
- 给每个类提供完整的一组操作
- 明确地使用继承来表现不同类之间的共同点
- 根据需要,决定是否写一个main函数作为程序入口
面向对象程序设计的基本概念
类(Class)是用来描述具有相同属性(Attribute)和方法(Method)对象的集合。对象(Object)是类(Class)的具体实例。
比如学生都有名字和分数,他们有着共同的属性。这时我们就可以设计一个学生类, 用于记录学生的名字和分数,并自定义方法打印出他们的名字和方法。
属性(Attribute): 类里面用于描述所有对象共同特征的变量或数据。比如学生的名字和分数。
方法(Method): 类里面的函数,用来区别类外面的函数, 用来实现某些功能。比如打印出学生的名字和分数
初识面向对象
python中一切皆为对象,类型的本质就是类,所以,不管你信不信,你已经使用了很长时间的类了
>>> dict #类型dict就是类dict
<class 'dict'>
>>> d=dict(name='abc') #实例化
>>> d.pop('name') #向d发一条消息,执行d的方法pop
'abc'
从上面的例子来看,字典就是一类数据结构,我一说字典你就知道是那个用{}表示,里面由k-v键值对的东西,它还具有一些增删改查的方法。但是我一说字典你能知道字典里具体存了哪些内容么?不能,所以我们说对于一个类来说,它具有相同的特征属性和方法。
而具体的{‘name’:‘eva’}这个字典,它是一个字典,可以使用字典的所有方法,并且里面有了具体的值,它就是字典的一个对象。对象就是已经实实在在存在的某一个具体的个体。
创建对象
要创建一个类我们需要使用关键词class. 比如我们定义一个学生类Student应该是这样的:
# 创建一个学生类
class Student:
# 定义学生属性,初始化方法
def __init__(self, name, score):
self.name = name
self.score = score
# 定义打印学生信息的方法
def show(self):
print("Name: {}. Score: {}".format(self.name, self.score))
要创建具体的学生对象(Object),我们还需要输入:
student1 = Student("John", 100)
student2 = Student("Lucy", 99)
在这个案例中,Student是类,student1和student2是我们创建的具体的学生对象。当我们输入上述代码时,Python会自动调用默认的__init__初始构造函数来生成具体的对象。
类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self。self代表类的实例,而非类。
当你创建具体的对象后,你可以直接通过student1.name和student1.score来分别获取学生的名字和分数,也可以通过student1.show()来直接打印学生的名字和分数。
print(student1.name)
student1.show()
得到的结果如下
John
Name:John.Score:100
类变量
假设我们需要在Student类里增加一个计数器number,每当一个新的学生对象被创建时,这个计数器就自动加1。由于这个计数器不属于某个具体学生,而属于Student类的,所以被称为类变量(class variables)。而姓名和分数属于每个学生对象的,所以属于实例变量(instance variables)。
class Student:
# number属于类变量,不属于某个具体的学生实例
number = 0
def __init__(self, name, score):
# name和score属于实例变量
self.name = name
self.score = score
Student.number = Student.number + 1
def show(self):
print("Name: {}. Score: {}".format(self.name, self.score))
student1 = Student("John", 100)
student2 = Student("Lucy", 99)
print(Student.number) # 打印2
注意:在调用类变量number时,需要使用Student.number来调用。
类方法
正如同有些变量只属于类,有些方法也只属于类,不属于具体的对象。我们在创建对象方法时使用的self是指对象本身。属于类的方法不使用self参数, 而使用参数cls,代表类本身。另外习惯上对类方法我们会加上@classmethod的修饰符做说明。
同样拿Student为例子,我们不用print函数打印出已创建学生对象的数量,而是自定义一个类方法来打印,我们可以这么做:
class Student:
number = 0
def __init__(self, name, score):
self.name = name
self.score = score
Student.number = Student.number + 1
# 定义打印学生信息的方法
def show(self):
print("Name: {}. Score: {}".format(self.name, self.score))
# 定义类方法,打印学生的数量
@classmethod
def total(cls):
print("Total:",cls.number)
student1 = Student("John", 100)
student2 = Student("Lucy", 99)
Student.total() # 打印
Total:2
到这里,你应该已经对面相对象编程已经有了一定的概念了,接下来让我们一起更深入地了解一下面向对象的三大特征
面向对象编程的三大特征
1.封装
封装是指将数据与具体操作的实现代码放在某个对象内部,外部无法访问。必须要先调用类才能启动。
比如在之前的student案例中,如果直接print(name)就会报错。
私有属性
类的私有属性
__privateAttrs:两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问。在类内部的方法中使用时 self.__privateAttrs。
类的私有属性实例如下:
class Student:
__Count = 0 # 私有变量
publicCount = 0 # 公开变量
def count(self):
self.__Count += 1
self.publicCount += 1
print (self.__Count)
counter = Student()
counter.count()
counter.count()
print (counter.publicCount)
print (counter.__Count) # 报错,实例不能访问私有变量
执行以上程序输出结果为:
1
2
2
私有方法
类的私有方法
__privateMethod:两个下划线开头,声明该方法为私有方法,只能在类的内部调用 ,不能在类的外部调用self.__privateMethods。
class Student:
def __init__(self, name, url):
self.name = name # public
self.__id = id # private
def who(self):
print('name : ', self.name)
print('id: ', self.__id)
def __show(self): # 私有方法
print('这是私有方法')
def show(self): # 公有方法
print('这是公有方法')
self.__show()
x = Student('MITA', '1')
x.who() # 正常输出
x.show() # 正常输出
x.__show() # 报错
执行以上程序输出结果为:
name:MITA
id:1
这是公有方法
这是私有方法
其中最后一句’这是私有方法’是因为公有方法show在内部调用私有方法__show产生的。而直接在外部调用__show是会报错的。
2.继承
什么是继承
继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类
比如,我们已经编写了一个名为Animal的class,有一个run()方法可以直接打印:
class Animal(object):
def run(self):
print('running...')
当我们需要编写Dog和Cat类时,就可以直接从Animal类继承:
class Dog(Animal):
pass
class Cat(Animal):
pass
继承有什么好处?最大的好处是子类获得了父类的全部功能。由于Animial实现了run()方法,因此,Dog和Cat作为它的子类,什么事也没干,就自动拥有了run()方法:
dog = Dog()
dog.run()
cat = Cat()
cat.run()
运行结果:
running
running
方法重写
如果你的父类方法的功能不能满足你的需求,你可以在子类重写你父类的方法:
class Dog(Animal):
def run(self):
print('Dog is running...')
class Cat(Animal):
def run(self):
print('Cat is running...')
调用结果:
Dog is running...
Cat is running...
当子类和父类都存在相同的run()方法时,我们说,子类的run()覆盖了父类的run(),在代码运行的时候,总是会调用子类的run()。这样,我们就获得了继承的另一个好处:多态。
3.多态
在上例中,Dog和Cat都是Animal的子类,是Animal就都会run,我们可以分别调用他们的run方法,也可以定义一个统一的接口来使用。
def Run(animal):
animal.run()
然后调用:
Run(dog)
Run(cat)
也可以得到结果
Dog is running...
Cat is running...
参考:
https://blog.csdn.net/weixin_42134789/article/details/80194788.
https://www.runoob.com/python3/python3-class.html
to be continued…