Python进阶:Python面向对象编程
我们在学习一种编程语言的时候,学会了配置集成开发环境、基本语法、基本的编程技巧,这个时候总会遇到深入学习这个编程语言的瓶颈。我在学习python编程的时候,有一段时间总是在上面的过程反复徘徊,这次狠下心,针对我遇到的第一个瓶颈——面向对象Python编程。
- 当我们能够使用
git
从github
上克隆现成的程序,进行本地开发,然后针对克隆不同的程序使用miniconda
、docker
搭建相应的虚拟环境。- 然而,面对一个团队或者公司开源的成百上千的代码模块,我们如何在短时间内熟练应用?秘诀就在于对Python面向对象编程的深入理解,只有掌握了面向对象编程,我们才能充分应用Pycharm类对象操作功能,极大的方便我们完成Python的开发。
一、Python面向对象简介
Python语言设计的初衷就是一种面向对象的编程语言,因此,在Python中创建一个类和对象相对于其他语言来说是很容易的。下面我们通过了一些面向对象的一些基本术语的示例,来体会Python面向对象编程的内涵。
首先,给出一个 最简单的类:
# Python中定义类的关键字为`class`,如果不对类进行任何定义,
# 可以直接输入pass。
class Dog:
pass
dog_1 = Dog() # 创建Dog()类的第一个实例`dog_1`
dog_2 = Dog() # 创建Dog()类的第二个实例`dog_2`
# 下面动态地给第一个实例赋予属性`name`:
d1.name = 'Xiaoliang'
# 输出第一个实例的`name`属性:
print(dog_1.name)
二、变量
Python类变量包括:类变量和实例变量。
2.1 实例变量
上面的动态给类添加变量,在实际应用会非常不方便,如果我们能直接在类中添加Dog
类的故有属性name
,也称为实例属性。
Python类实例变量的添加是通过__init__
函数(一种构造方法)实现的,假设一个狗天生具有的实例属性包含:name
(名字)、height
(高度)、blood
(血量)、power
(攻击力),实现代码如下所示:
class Dog:
# 构造方法,在使用`Dog`类实例化的时候,首先执行`__init__`函数
def __init__(self, name, height, blood, power):
# 实例化的时候,下面指令可以将定义的属性,赋值给自己;
# `self.name`是每个实例的名字,而`=`后面的`name`
# 是实例变量,它会根据不同的实例属性分配不同的值。
self.name = name
self.height = height
self.blood = blood
self.power = power
dog_1 = Dog('Xiaoliang', 0.6, 9, 5) # 创建第一个实例`dog_1`
dog_2 = Dog('Xiaowang', 0.7, 7, 3) # 创建第一个实例`dog_2`
print(dog_1); print(dog_2); print(dog_1.name); print(dog_2.name)
注意:类的实例变量是通过
self
关键字表示的,self
的主要作用是表明这个变量是与实例属性相关的变量。
2.2 类变量
1、给类添加类变量的方法
这里结合 实例变量 来理解 类变量,来理解类变量。比如下面的代码,这里多了一个类属性num_of_dogs
:
class Dog:
num_of_dogs = 0 #类属性
# 构造方法:添加实例属性,做其他的初始工作
def __init__(self, name, height, blood, power):
self.name = name
self.height = height
self.blood = blood
self.power = power
dog_1 = Dog('Xiaoliang', 0.6, 9, 5) # 创建第一个实例`dog_1`
dog_2 = Dog('Xiaowang', 0.7, 7, 3) # 创建第一个实例`dog_2`
# 访问类属性的方法:
# (1)通过类名访问
print(Dog.num_of_dogs)
# (2)通过实例访问
print(dog_1.num_of_dogs)
2、访问类变量的方法
(1)通过类名访问
Dog.num_of_dogs
;
(2)通过实例访问dog_1.num_of_dogs
。
3、修改类变量的方法
同样有两种修改方法:
(1)通过类名修改:
Dog.num_of_dogs = 10 #通过类名修改
print(Dog.num_of_dogs)
print(dog_1.num_of_dogs, dog_2.num_of_dogs)
产生这种结果的原因:
这是由于Dog.num_of_dogs = 10
执行完了,类Dog
的类属性num_of_dogs
就成为了10,由于类属性的作用域在类中是全局的,所以实例dog_1
和dog_2
在访问类属性num_of_dogs
也会输出10。
(2)通过实例修改
dog_1.num_of_dogs = 10
print(Dog.num_of_dogs)
print(dog_1.num_of_dogs, dog_2.num_of_dogs)
通过实例修改类属性的输出结果,与通过类名修改的结果不一样,这里只有dog_1.num_of_dogs
输出10
,而其他的类属性仍然为0
。
产生这种结果的原因:
这是由于dog_1.num_of_dogs = 10
执行完了,生成了dog_1
实例一个实例属性num_of_dogs
,所以dog_1.num_of_dogs
在输出的时候首先访问的是dog_1.num_of_dogs
这个实例属性,而不是类属性Dog.num_of_dogs
。
4、类变量的作用
类变量num_of_dogs
可以用来统计类Dog
创建了多少个实例dog_x
。可以在类中的__init__
函数实现,代码如下所示:
class Dog:
num_of_dogs = 0
def __init__(self, name, height, blood, power):
......
num_of_dogs = num_of_dogs + 1
三、方法
3.1 实例方法
除了上面在__init__
函数中添加的Dog
类的属性,还可以通过def
函数方法添加类的方法,比Dog
类不仅具有上面四中故有属性,假设它们还会说人话,下面通过def
函数添加speak()
方法:
class Dog:
def __init__(self, name, height, blood, power):
self.name = name
self.height = height
self.blood = blood
self.power = power
def speak(self):
# 构造`speak()`方法:
# self.xx是每个实例的属性;所以输出实例的属性,
# 需要使用self.xx
print(f'我是{self.name}, 身高{self.height},
血量{self.blood}, 攻击力{self.power}')
dog_1 = Dog('Xiaoliang', 0.6, 9, 5) # 创建第一个实例`dog_1`
dog_2 = Dog('Xiaowang', 0.7, 7, 3) # 创建第一个实例`dog_2`
# 下面调用一下每个实例的类方法:
print(dog_1.speak()); print(dog_2.speak())
3.2 类方法
假设我们有这样一个应用场景:我们不仅要能够得到一共实例化了多少个实例,还需要能够输出所有实例的对象,使用类方法(标志符为@classmethod
)解决该问题的程序如下所示:
class Dog:
# 首先定义一个类列表变量,用于存放类实例
dogs = []
# 构建一个类方法用于统计一共有多少个实例对象
@classmethod #这个关键字表示下面的函数为类方法
def num_of_dogs(cls): # cls表示输入为一个类对象
return len(cls.dogs)
def __init__(self, name)
self.name = name
# 下面指令实现:每次实例化一个实例时,就将这个实例放入
# 类变量dogs[]列表中。注意:`self`表示当下的实例对象
Dog.dogs.append(self)
dog_1 = Dog('Xiaoliang'); dog_2 = Dog('Xiaowang')
dog_3 = Dog('Xiaoli'); dog_4 = Dog('Xiaozhao')
# 使用类名调用Dog类的类方法num_of_dogs
print(Dog.num_of_dogs)
# 当然我们也可以使用实例来调用类方法num_of_dogs
print(dog_1.num_of_dogs)
注意:类方法不可以访问实例变量,也就是跟
self
相关的变量,它只可以访问类变量,所以类方法的设计只是针对类的。
3.3 静态方法
实例方法是针对实例变量(以self
标志)设计的方法,而类方法是针对类变量(以cls
标志)进行设计的方法。但是在实际中,有时我们需要设计一种类方法既不对实例变量进行处理,也部队类变量进行处理,这就是所谓的 静态方法,其标志符为@staticmethod
。比如我们要在类中添加一个解释性语言:
class Dog:
@staticmethod
def description()
print("This is a Dog class!")
def __init__(self, name):
self.name = name
四、面向对象三大特征
4.1 继承
虽然,前面设计Dog
类方法,实例化变量有:name
、height
、blood
、power
和类方法speak()
。但是对于下图所示,对于不同类型的狗,它们具Dog
的各种属性,但是它们又有自己的属性和方法。
|
|
|
由此仅仅使用Dog
类并不能很好的表征所有的狗对象,所以我们需要引入面向对象编程的一个非常重要的概念——类的继承。使用类的继承,我们就可以在Dog
类方法上,实现对宠物狗、牧羊犬、警犬的表征。下面以宠物狗为例,解释类的继承,代码如下所示:
class Dog:
def __init__(self, name, height, power):
self.name = name
self.height = height
self.power = power
def speak(self):
print(f'我是{self.name}, 身高{self.height}, 攻击力{self.power}')
# 在宠物狗类PetDog后面加上一个括号,内部写入父类Dog
# 这样就实现了继承了Dog类的PetDog类:
class PetDog(Dog):
# 对于宠物狗的独有实例属性price,需要定义在
# __init__()函数中定义
def __init__(self, name, height, power, price):
# super()关键字表示PetDog类继承父类的参数!
super().__init__(name, height, power)
# 执行完上面的指令,下面只需要指配宠物狗类PetDog
# 独有的实例变量price即可。
self.price = price
# 下面创建一个宠物狗实例
pet_dog = PetDog('Xiaoxin', 0.6, 1, 10000)
# 基于PetDog类创建的pet_dog实例,由于super()函数的作用,
# 也继承了父类的name、height、power实例变量,当然,它也
# 具有自己特有的实例变量。
print(pet_dog.price, pet_dog.name, pet_dog.height, pet_dog.power)
# pet_dog同样也继承了父类的speak()方法!
pet_dog.speak()
4.2 封装
封装就是只能在类内部访问,外部访问属性或方法会报异常,python中的封装很简单,只要在属性前面或方法名前加上两个下划线就可以,这样就完成了属性和方法的私有化,也就是封装。其实Python语言并没有严格意义上的私有属性,即便是类中定义了私有属性,我们也可以通过类方法输出结果。
下面给出一个Python类的封装例子,其中实例变量price
被设置成私有属性,但是我们可以通过get(self)
类方法输出私有属性;另外设置一个私有类函数secret_language
,并设置一个共有类方法speak
:
class Dog:
def __init__(self, name, price):
self.name = name
# 私有实例变量
self.__price = price
# 定义一个类方法,外部可以调用该类方法输出私有实例变量
def get(self):
print(self.__price)
dog_1 = Dog("Xiaoliang", 1000)
print(dog_1.name) # 共有变量正常输出“Xiaoliang”
print(dog_1.__price) # 私有实例变量不能被外部直接输出
print(dog_1.get()) # 正常输出私有实例变量`price`的值
验证输出结果如下图所示:
4.3 多态
Python中本质上是没有真正多态这种思路,只是形式上有这样一个多态的思路。