在上一篇《手把手陪您学Python》43——类的继承中,我们学习了父类和子类的继承关系,同时再一次见证了__init__()方法的神奇之处。
今天,我们将介绍面向对象编程最后一部分的内容,也就是类的导入。
之前我们在《手把手陪您学Python》30——模块中介绍过模块的导入,也就是在我们的程序文件中使用其他文件中的函数。
顾名思义,类的导入就是在我们的程序文件中,通过模块的导入,使用其他文件中的类。
虽然我们上节课说过,要建立子类继承父类的属性和方法,就要在一个文件中同时包括父类和子类,而且父类要在子类之前。但是当我们创建大型项目时,会涉及大量父类和子类继承关系的定义,如果都写在一个文件中,必然会导致代码量的激增。
此时,就需要我们在当前程序文件中通过导入方式,引入其他文件中的类,特别是标准化或者是其他开发者已经编译好的类,以简化程序文件中的代码量。
这就是导入类的意义,也和导入模块中的函数的意义是一样的。这时我们对模块的认识也有了相应的扩展,模块中不仅有大量Python开发者已经编写好的函数,还有很多强大的类供我们使用。
由于导入类与导入函数的模式非常相似,所以在下面介绍类的导入时,就直接借用了当时介绍模块时导入函数的内容结构与文字描述,方便大家的前后对比。
1、创建类
要让类是可导入的,就得先创建包括类的模块。
模块是扩展名为.py的文件,包含要导入到程序中的代码。
由于Jupyter Notebook保存文件的扩展名是.ipynb,是不能够作为模块保存和导入的,所以在制作模块文件时,需要我们使用PyCharm软件编写代码和保存模块。在导入类进行使用时,可以随意使用Jupyter Notebook或PyCharm。
下面就让我们一起创建一个包含Mario和Enemy两个类的模块:
In [1]: """定义马里奥类和敌人类"""
class Mario:
def __init__(self, life, cap, name, food):
self.life = life
self.cap = cap
print("{} has {} lifes.".format(name, self.life))
print("{} has a {} cap.".format(name, self.cap))
self.jump()
self.eat(name, food)
self.attack(name)
def jump(self):
print("Mario is jumping!")
def attack(self,name):
print("{} attacked an anomy!".format(name))
def eat(self, name, food):
print("Mario ate a {}!".format(food))
print("{} became bigger!".format(name))
class Enemy:
number = "many"
def attack(self):
print("What a pity! Mario was killed by the enemy~~~")
将这个代码段在PyCharm中保存为mario.py(默认就是保存为.py),这就是我们创建的包含着类模块,一会儿我们会多次导入这个模块,并引入其中的类、属性、方法以及创建子类。
2、导入类的方法
导入类的方法有很多种,下面就对每种方法进行逐一的介绍。
a、导入整个模块
在mario.py所在的目录中,创建一个名为mario_a.py的文件。(如果用Jupyter Notebook创建,文件名就是mario_a.ipynb,都可以。)
使用import语句导入刚才创建的模块,就可以调用里面的类了。
为了全面展示导入类的效果和作用,在后面的示例中,对于Mario类,我们会进行继承的演示;对于Enemy类,我们会进行实例的引用。
In [2]: import mario
class Super_Mario(mario.Mario): # 调用模块中的Mario类创建子类
def __init__(self, life, cap, name, food):
super().__init__(life, cap, name, food)
print("\n***The result form Mario:\n")
Super_big_Mario = Super_Mario(30, "White", "Super Mario", "red mushroom") # 将子类实例化
print("\n***The result form Enemy:\n")
enemy = mario.Enemy() # 调用模块中的Enemy类并实例化
print("There are {} enemies!".format(enemy.number)) # 引用类属性
enemy.attack() # 引用类方法
当运行这段代码时,import mario让Python打开mario.py文件,并将其中所有类都复制到当前程序中,也就实现了从其他文件或者模块调用类的目的,如果导入的类的位置在定义子类之前,也满足父类要在子类之前的要求。其语法格式为:
import 模块名
我们不需要了解这个复制、调用的过程是如何实现的,只需要知道在mario_a.py这个文件的代码中,可以使用mario.py中定义的所有类了。
要调用被导入模块中的某一特定类,需要将被导入模块的名称和要使用的类名称之间用英文句点“.”进行连接,语法格式为:
模块名.类名()
这样的语法格式,和我们之间介绍的“对象.方法()”非常类似,这也是Python程序易于编写,可读性高的一个体现。
上面的代码中,mario.Mario和mario.Enemy就是调用的mario.p中的两个函数的过程。
运行上面的程序,可以得到下面的结果:
Out[2]: ***The result form Mario:
Super Mario has 30 lifes.
Super Mario has a White cap.
Mario is jumping!
Mario ate a red mushroom!
Super Mario became bigger!
Super Mario attacked an anomy!
***The result form Enemy:
There are many enemies!
What a pity! Mario was killed by an enemy~~~
为了输出结果的美观,我们在实例化前,都增加了一个打印内容,以区分来自于模块中的不同的类。由于输出结果类似,在后面的实例中就不再展示输出结果了,大家可以自行运行程序查看输出结果。
这就是第一种导入类的方法,只需编写一条import语句,并在其中指定模块,就可以在程序中使用该模块中的所有类了。
b、导入特定函数
除了将整个模块进行导入外,还可以导入模块中的特定类,这种导入方法的语法格式为:
from 模块名 import 类名
如果用逗号“,”分隔类名,还可以根据需要,从模块中导入任意数量的函数,其语法格式为:
from 模块名 import 类名1, 类名2, 类名n
对于我们之前建立的mario.py模块,如果只想导入Mario类,可以再创建一个文件mario_b.py,并编写如下代码:
In [3]: from mario import Mario
class Super_Mario(Mario): # 调用模块中的Mario类创建子类,但不需要写模块名了
def __init__(self, life, cap, name, food):
super().__init__(life, cap, name, food)
print("\n***The result form Mario:\n")
Super_big_Mario = Super_Mario(30, "White", "Super Mario", "red mushroom")
可以看到,使用这种方法时,就不需要再使用“模块名.类名()”的语法格式了,而是像普通类一样,直接使用类名调用类就可以了。
这是因为,无论我们导入的是一个模块还是多个模块,如果我们要调用其中的类,需要使用“模块名.类名()”的格式先指定模块,再指定类,即使只导入一个模块也一样。而当我们导入指定的类时,那么就没有模块名的事了,直接调用类名就可以了。
c、使用as给函数指定别名
由于要导入的模块的类名称是已经定义好,或者说通用的,可能并不能满足于我们某一个程序的命名规则,还有可以与我们现有程序中的类名称冲突。此时,就可以使用as关键字,给我们从其他模块中调用的类进行命名,相当于起了个“外号”,其语法格式为:
from 模块名 import 类名 as 别名
如果要导入多个类并进行重命名,就可以写为:
from 模块名 import 类名1 as 别名1, 类名2 as 别名2, 类名n as 别名n
对于之前建立的模块,我们可以再创建一个文件mario_c.py,来实现这种导入功能:
In [4]: from mario import Mario as Ma, Enemy as En
class Super_Mario(Ma): # 调用模块中的Mario类创建子类,但类使用的是别名
def __init__(self, life, cap, name, food):
super().__init__(life, cap, name, food)
print("\n***The result form Mario:\n")
Super_big_Mario = Super_Mario(30, "White", "Super Mario", "red mushroom") # 将子类实例化
print("\n***The result form Enemy:\n")
enemy = En() # 调用模块中的Enemy类并实例化,但类使用的是别名
print("There are {} enemies!".format(enemy.number))
enemy.attack()
我们可以看到,通过给类重命名(起别名),不仅可以解决合理命名的问题,还可以简化类的名称,便于使用。当然,简化也要在保证不影响可读性的情况下进行。
d、使用as给模块指定别名
除了给类指定别名,还可以给模块指定简化的别名,便于我们在导入整个模块,并调用类时使用,其语法格式为:
import 函数名 as 别名
当我们使用这种导入方法时,就可以在mario_d.py中使用如下代码:
In [5]: import mario as ma
class Super_Mario(ma.Mario): # 调用模块中的Mario类创建子类,但模块使用的是别名
def __init__(self, life, cap, name, food):
super().__init__(life, cap, name, food)
print("\n***The result form Mario:\n")
Super_big_Mario = Super_Mario(30, "White", "Super Mario", "red mushroom")
print("\n***The result form Enemy:\n")
enemy = ma.Enemy() # 调用模块中的Enemy类并实例化,但模块使用的是别名
print("There are {} enemies!".format(enemy.number))
enemy.attack()
导入模块和调用类的方式和介绍的第一种方法是一样的,只不过用简化的模块名称ma代替了原模块名称。
因为在一个程序中会导入的模块远比其中的类要少的多,所以一般使用简化模块名称的方式,而不是简化类名称的方式来编写代码,这样既达到了简化代码的目的,也不会影响程序的可读性。
所以,这种导入方式也是Python中最为常用的导入方式,包括我们在数据分析中,就会经常用到:
import numpy as np
import pandas as pd
这样的导入模块的语句。
e、导入模块中的所有类
使用“*”可以导入模块中的所有类,然后就可以像在一个文件中写的类一样使用他们了,其语法格式为:
from 模块名 import *
最后建立一个mario_e.py的文件,编写如下代码:
In [6]: from mario import *
class Super_Mario(Mario): # 调用模块中的Mario类创建子类,但不需要写模块名称了
def __init__(self, life, cap, name, food):
super().__init__(life, cap, name, food)
print("\n***The result form Mario:\n")
Super_big_Mario = Super_Mario(30, "White", "Super Mario", "red mushroom")
print("\n***The result form Enemy:\n")
enemy = Enemy() # 调用模块中的Enemy类并实例化,但不需要写模块名称了
print("There are {} enemies!".format(enemy.number))
enemy.attack()
由于导入了模块中的所有类,在程序中可以直接使用名称调用每个类,而无需再用“.”来引入了。
但是,当我们要使用不是我们自己编写的类,特别是大型模块时,最好不要采用这种导入方法。因为要导入的模块中可能包括大量我们不确认的类,其名称可能会与我们当前程序中的类名称冲突。当遇到这种重复的类名称时,Python会覆盖其中的一部分类,而不是分别导入所有的类,可能导致一些我们意料不到的结果。
所以,最好的方式还是导入整个模块,并通过模块名和类名的组合方式,调用并使用类。当然,如果需要,还可以指定模块名称,达到简化代码的目的。
以上就是通过导入模块来调用其他文件中的类的介绍,同时,我们也再一次见证了Python中强大的库的调用原理,为我们后面学习数据分析及其他Python应用奠定了基础。
至此,我们有关面向对象编程的介绍就告一段落了,虽然只有短短6篇文章的内容,但基本上把面向对象编程的主要思想、类的定义、属性、方法、继承、导入都进行了介绍。也许做不到让大家能够对面向对象编程有什么深入的认识和理解,但能够将面向对象编程的基础知识介绍给大家,能够和大家一起走进面向对象编程的大门,就已经达到目的了。
面向对象编程是一种非常先进的编程思想,特别是在一些大型项目中,往往都会采用面向对象编程的思想进行开发。如今,我们已经掌握了面向对象编程的基础知识,剩下的就是我们在日常的代码编写和项目开发的过程中的应用了。
讲到这里,不仅面向对象的内容介绍完了,我们整个Python入门部门的内容也基本结束了。非常感谢大家的支持和一路的陪伴,让我也受益良多。
下一篇,我们将会一起回顾下Python入门阶段所学的内容,为我们的Python入门做一个总结,敬请关注。
感谢阅读本文!如有任何问题,欢迎留言,一起交流讨论^_^
要阅读《手把手陪您学Python》系列文章的其他篇目,请关注公众号点击菜单选择,或点击下方链接直达。
《手把手陪您学Python》3——PyCharm的安装和配置
《手把手陪您学Python》5——Jupyter Notebook
For Fans:关注“亦说Python”公众号,回复“手44”,即可免费下载本篇文章所用示例语句。
