第3章  类与模块	

3.1  类 和 对 象
  Python采用了面向对象程序设计的思想,以类和对象为基础,将数据和操作封装成一个类,通过类的对象进行数据操作。
3.1.1  类的格式与创建对象
  1.类的一般形式 
  类由类声明和类体组成,而类体又由成员变量和成员方法组成,其一般形式如下:
  
     class  类名:
          成员变量
          def  成员方法名(self)
  
  在类声明中,class是声明类的关键字,表示类声明的开始,类声明后面跟着类名,按习惯类名要用大写字母开头,并且类名不能用阿拉伯数字开头。 
  在类体中定义的成员方法与在类外定义的函数一般形式是相同的。也就是说,通常把定义在类体中的函数称为方法。
  类中的self在调用时代表类的实例,与C++或Java中的this作用类似。
  2.创建类的对象
  类在使用时,必须创建类的对象,再通过类的对象来操作类中的成员变量和成员方法。
  创建类对象的格式为:
  
     对象名 = 类名()
  
  3.调用类的成员方法
  调用类的成员方法时,需要通过类对象调用,其调用格式如下:
  
     对象名.方法名(self)
  
  【例3.1】 编写一个计算两数之和的类。

  
     class Myclass:
     	def sum(self, x, y):
     		self.x = x
     		self.y = y
     		return self.x + self.y
     
     obj = Myclass()
     s = obj.sum(3, 5)
     print('s =',s)
     
  在程序的类定义中,方法sum(self,x,y)的参数self代表类对象自身,self.x = x 即把赋值语句右边的参数x值赋值给左边类成员变量x。为了区分参数及成员变量,在成员变量x前面添加self。 
  程序运行结果如下:
  
     s = 8
  
  4.类的公有成员和私有成员
  在Python程序中定义的成员变量和方法默认都是公有成员,类之外的任何代码都可以随意访问这些成员。如果在成员变量和方法名之前加上两个下画线“_ _”作前缀,则该变量或方法就是类的私有成员。私有成员只能在类的内部调用,类外的任何代码都无法访问这些成员。
  【例3.2】 私有成员示例。
  
     class testPrivate:  
     	def _ _init_ _(self, x, y):                 
     		self._ _x = x
     		self._ _y = y
     	def add(self):  
     		self._ _s = self._ _x + self._ _y
     		return self._ _s
     	def printData(self):  
     		print (self._ _s)  
        
     t = testPrivate(3, 5)   # 创建类对象
     s = t.add()     
     t.printData()  
     print('s = ',s) 
  
  程序运行结果如下:
  
     8
     s = 8
  
  5.类的构造方法
  在Python中,类的构造方法为 _ _init_ _( ),其中方法名开始和结束的下画线是双下画线。构造方法属于对象,每个对象都有自己的构造方法。
  如果一个类在程序中没有定义_ _init_ _( )方法,则系统会自动建立一个方法体为空的  _ _init_ _( )方法。
  如果一个类的构造方法带有参数,则在创建类对象时需要赋实参给对象。
  在程序运行时,构造方法在创建对象时由系统自动调用,不需要用调用方法的语句显式调用。
  【例3.3】 设计一个员工类Person。该类有Name(姓名)和Age(年龄)两个变量,可以从键盘输入员工姓名、年龄等信息。
  程序代码如下:
  
     class Person: 
         def _ _init_ _(self, Name, Age):
             self.name = Name
             self.age = Age
         def main(self):
             print(self.name)
             print(self.age)
     
     name = input('input  name:')
     age = input('input age:')
     p = Person(name,age)   
     p.main()   # 调用类体中的方法  
  
  程序运行结果如下:
     
     input name:  sundy
     input age:  22
     sundy
     22
  
  6.析构方法
  在Python中,析构方法为 _ _del_ _ ( ),其中开始和结束的下画线是双下画线。析构方法用于释放对象所占用的资源,在Python系统销毁对象之前自动执行。析构方法属于对象,每个对象都有自己的析构方法。如果类中没有定义_ _del_ _( )方法,则系统会自动提供默认的析构方法。
  【例3.4】 析构方法示例。
  程序代码如下:
  
     class Mood:
         def _ _init_ _(self,x):
             self.x=x
             print('产生对象',x)
     
         def _ _del_ _(self): 
             print('销毁对象', self.x)
     
     f1 = Mood(1)
     f2 = Mood(2)
     del f1
     del f2
  
  程序运行结果如下:
  
     产生对象1
     产生对象2
     销毁对象1
     销毁对象2 
3.1.2  类的继承
  类的继承是为代码复用而设计的,是面向对象程序设计的重要特征之一。当设计一个新类时,如果可以继承一个已有的类,无疑会大幅度减少开发工作量。
  在继承关系中,已有的类称为父类或基类,新设计的类称为子类或派生类。派生类可以继承父类的公有成员,但不能继承其私有成员。
  在继承中,父类的构造方法_ _init_ _( )不会自动调用,如果在子类中需要调用父类的方法,可以使用内置函数super()或通过“父类名.方法名( )”的方式实现。
  1.类的单继承
  类的单继承的一般形式为:
  
     class 子类名(父类名):
        子类的类体语句
  
  【例3.5】 定义一个父类Person,再定义一个子类Sunny继承Person,并在子类中调用父类的方法。 
  程序代码如下:
  
     class Person: 
         def _ _init_ _(self, Name, Age):
             self.name = Name
             self.age = Age
         def main(self):
             print('姓名:', self.name)
             print('年龄:', self.age)
         
     class Sunny(Person):
     	def _ _init_ _(self, name, age, score):
           super(sunny, self)._ _init_ _(name, age)  
     		self.score = score
     def prn(self):
     		Person.main(self)
     		print('成绩:', self.score)
     
     name = input('请输入姓名: ')
     age = input('请输入年龄: ')
     score = input('请输入成绩: ')
     s = Sunny(name,age, score)  	# 实例化子类对象
     s.prn()        					# 调用子类的方法
  
  程序运行结果如下:
  
     请输入姓名:  张大山
     请输入年龄:  22
     请输入成绩: 88
     姓名: 张大山
     年龄: 22
     成绩: 88
  
  2.类的多继承
  Python支持多继承,多继承的一般形式为:
  
     class  子类名(父类名1,父类名2, …, 父类名n):
        子类的类体语句
  
  Python在多继承时,如果这些父类中有相同的方法名,而在子类中使用时没有指定父类名,则Python系统将从左往右按顺序搜索。
  【例3.6】 多继承示例。
  程序代码如下:
  
     class A:
         def _?_init_?_(self):
             self.one="第一个父类"
     class B:
         def _?_init_?_(self):
             self.two="第二个父类"
     
     class C(A,B):
         def _?_init_?_(self):
             A._?_init_?_(self)
             B._?_init_?_(self)
         def prn(self):
             print(self.one, '\n', self.two)
     
     subc=C()
     subc.prn()
  
  程序运行结果如下:
  
     第一个父类
     第二个父类
3.1.3  运算符重载
  Python语言提供了运算符重载功能,大大增强了语言的灵活性。运算符重载就是重新定义运算法则。在Python中,重载加法运算使用_?_add_?_()方法定义运算法则,重载减法运算使用_?_sub_?_()方法运算法则。
  【例3.7】 设有两个二维元组:(7,10)和(5,?2),它们的加法运算法则为对应元素相加。它们的减法运算法则为对应元素相减。编写程序,计算这两个元组相加、相减的值。
  程序代码如下:
  
     class Vector:
        def _?_init_?_(self, a, b):
           self.a = a
           self.b = b
      
        def _?_str_?_(self):
           return 'Vector (%d, %d)' % (self.a, self.b)
        
        def _?_add_?_(self,other):
           return Vector(self.a + other.a, self.b + other.b)
     
        def _?_sub_?_(self,other):
           return Vector(self.a - other.a, self.b - other.b)
      
     v1 = Vector(7,10)
     v2 = Vector(5,-2)
     print(v1 + v2)
     print(v1 - v2)
  
  程序运行结果如下:
  
     Vector(12, 8)
     Vector(2, 12)
3.2  模    块
  一个较大型的程序通常都是由许多功能函数或类组成的,为了方便程序开发团队分工协作,可以将所建的函数或类保存为模块(module)形式的独立文件,未来可以供其他程序调用。模块的扩展名为py。
3.2.1  函数模块及函数模块的导入
  1. 建立函数模块
  在Python中,每个包含有函数的Python文件都可以作为一个模块来使用,其模块名就是文件名。
  【例3.8】 创建函数模块示例。
  设有Python文件hello.py,其中包含hh()函数,代码如下:
  
     def hh():
     	    str="你好, Python!"
     	    return str
  
  这样,就建立了一个名为hello的模块,其中的hh()函数可以供其他程序调用。
  2. 模块的导入
  在Python中用关键字import来导入某个模块,其导入模块的形式有两种。
  1)用import形式导入模块中的所有函数
  用import导入模块的一般形式为:
  
     import  模块名
  
  比如要引用例3.8中的模块hello,就可以在文件最开始的地方用
  
     import hello
  
语句来导入。
  在调用import导入模块的函数时,必须使用以下形式来调用:
  
     模块名.函数名
  
  例如,调用hello模块中的hh()函数。
  
     import hello
     s = hello.hh()
     print(s)
  
  2)用 from…import…形式导入模块中指定的函数或变量
  用 from…import…导入模块的一般形式为:
  
     from  模块名 import  函数名或变量名
  
  比如要引用模块math中的sqrt()函数,可以用
  
     from  math  import sqrt()
  
语句来导入。
  在调用from…import…导入模块的函数时,直接使用函数名来调用模块中的函数,而不需要在函数的前面加上模块名。
  【例3.9】 编写一个计算两数之和的模块,再在另一个程序中调用该模块。
  (1)编写模块代码,其中包含有计算两数之和的函数sum(),保存为ex3_9_1.py。
  
     def sum(n1, n2):
     	s = n1 + n2
     	return s
  
  (2)编写调用模块程序ex3_9_2.py,其代码如下:
  
     import ex3_9_1
     ss = ex3_9_1.sum(3, 5)
     print("3 + 5 =",ss)
  
  程序ex3_9_2.py的运行结果为:
  
     3 + 5 = 8
  
3.2.2  类模块
  类模块与函数模块类似,将类保存为独立的Python文件,其他程序则通过导入模块语句,调用模块中的类。
  1. 用 from…import…形式导入类模块
  导入类模块的语法格式与导入函数模块的语法格式相同,它的语法如下:
  
     from  模块名 import  类名
  
  【例3.10】 设有一个包含银行类的模块,编写程序调用该模块。
  (1)编写模块代码,其中包含有银行储户信息类,保存为ex3_10_Banks.py。
  
     # 包含银行储户信息类的模块
     class Banks():
     	def __init__(self, cname):  	# 初始化
     		self.__name = cname  		# 储户姓名
     		self.__amount = 0  			# 储户总金额
     
     	def save_money(self, money):  	# 存款操作
     		self.__amount += money    	# 增加总金额
     		print("存款", money, "元,目前余额:", self.__amount) # 显示存款信息
     	
     	def get_money(self, money):   	# 取款操作
     		self.__amount -= money    	# 减少总金额 
     		print("取款", money, "元,目前余额:", self.__amount) # 显示存款信息
  
  (2)编写调用模块程序ex3_10_2.py,其代码如下:
  
     from ex3_10_banks import Banks  	# 导入模块中的Banks类
     
     zdsbank = Banks("张大山")    		# 声明Banks类对象
     zdsbank.save_money(8000)     		# 存款为8000元
  
  程序ex3_10_2.py的运行结果为:
  
     存款8000元,目前余额: 8000
  
  2. 用import形式导入类模块
  与用import导入函数模块的形式相同,也可以使用如下语法格式导入类模块:
  
  import  模块名
  
  比如要引用例3.10的类模块ex3_10_banks,就可以在文件最开始的地方用
  
  import ex3_10_banks
  
语句来导入。
  在调用import导入模块的类时,必须使用以下形式来调用:
    
     模块名.类名
  
  例如,编写用import导入类模块ex3_10_banks的程序ex3_10_3.py,其代码如下:
  
     import ex3_10_banks                       		# 导入ex3_10_banks模块 
     
     zdsbank = ex3_10_banks.Banks("张大山")    	# 声明Banks类对象
     zdsbank.save_money(8000)                  	# 存款为8000元
  
  程序ex3_10_3.py的运行结果为:
  
     存款8000元,目前余额: 8000
  
3.2.3  常用标准库模块及导入模块的顺序
  1. 常用标准库模块
  Python系统的标准库中定义了很多模块, 从 Python 语言自身特定的类型和声明, 到一些只用于少数程序的不著名的模块,林林总总有200多个。
  表3.1 列出了一些常用的标准库模块。
表3.1  一些常用的标准库模块
模块分类
模块名称
说    明
核心模块
os模块
os模块中的大部分函数通过对应平台相关模块实现,其常用方法有open()、file()、listdir()、system()等函数

sys模块
sys模块用于处理 Python 运行时环境。例如,退出系统时,使用命令:sys.exit(1)

time模块
math 模块实现了许多对浮点数的数学运算函数。例如,使用math模块的sqrt()函数进行开平方根的运算线程与进程模块
threading 模块
threading 模块为线程提供了一个高级接口, 只需要继承 Thread 类, 定义好 run()方法, 就可以创建一个新的线程

queue 模块
queue 模块提供了一个线程安全的队列 (queue) 实现。通过它可以在多个线程里安全地访问同一个对象
续表    
模块分类
模块名称
说    明


网络协议
模块
socket 模块
socket 模块实现了网络数据传输层的接口,使用该模块可以创建客户端或是服务器的套接字Socket通信

socketserver 模块
socketserver 为各种基于Socket套接字的服务器提供了一个框架,该模块提供了大量的类对象,可以用它们来创建不同的服务器

urllib 模块
urllib 模块为HTTP、FTP以及Gopher 提供了一个统一的客户端接口,它会自动地根据 URL 选择合适的协议处理器

httplib 模块
httplib 模块提供了一个HTTP客户端接口

webbrowser 模块
webbrowser模块提供了一个到系统标准Web浏览器的接口。它提供了一个open()方法, 接收文件名或URL作为参数, 然后在浏览器中打开它
  
  2. 导入模块的顺序
  当设计的程序需要导入多个模块时,应按照下面的顺序依次导入模块:
   (1)导入 Python系统的标准库模块,如os、sys等;
   (2)导入第三方扩展库模块,如pygame、mp3play等;
   (3)导入自己定义和开发的本地模块。
3.2.4  使用pip安装和管理扩展模块
  1.安装pip
  Python安装第三方的模块,大多使用包管理工具pip进行安装。Python包管理工具pip提供了对 Python 包的查找、下载、安装、卸载的功能。
  pip 下载地址为https://pypi.python.org/pypi/pip#downloads。选择 pip-9.0.1.tar.gz 文件进行下载。 
  下载完成后,将解压的文件保存到一个文件夹,使用控制台命令窗口进入解压目录,输入安装命令:
  
     python setup.py install
  
  pip安装完后还需要配置环境变量,这样pip指令才能生效。找到Python安装路径下的scripts 目录,复制该路径。例如:
  
     C:\Users\pandap\AppData\Local\Programs\Python\Python36-32\Scripts
  
将其添加到系统环境变量path中。
  2.通过 pip 安装扩展模块
  当前,pip已经成为管理Python扩展模块的主要方式。常用pip命令如表3.2所示。
  
表3.2  常用pip命令
pip命令
说明
pip命令
说明
install
安装模块
list
列出已安装模块
download
下载模块
show
显示模块详细信息.
uninstall
卸载模块
search
搜索模块
freeze
按着一定格式输出已安装模块列表
help
帮助
  
  例如:
  (1)安装MySQL数据库管理模块:
  
     pip install pymysql	
  
  (2)安装图形处理库模块:
  
     pip install pillow
  
  (3)安装SomePackage模块:
  
     pip install SomePackage  
  
  (4)卸载SomePackage模块:
  
     pip uninstall SomePackage 
  
  (5)查看当前已经安装的模块:
  
     pip list
  
  查看当前已经安装的模块命令运行结果如图3.1所示。

图3.1  查看已经安装的模块
3.2.5  使用Anaconda安装和管理扩展模块
  Anaconda是由Python管理的开源数据科学平台,其中包含了180个科学计算模块及其依赖项,可以用它来安装和管理扩展模块。
  1. 下载和安装Anaconda
  1)下载Anaconda安装包
  Anaconda安装包的下载地址为https://www.anaconda.com,该网站提供了Python 2.7和Python 3.7两个版本的安装程序,这里选择安装Python 3.7版。
  2)安装Anaconda
  Anaconda的安装比较简单,双击安装包后,按安装向导的指引即可以完成安装。
  3)配置环境变量
  完成Anaconda的安装后还需要配置环境变量,Anaconda才能有效使用。其配置方法与pip的配置方法相同,在系统的环境变量path中,添加Anaconda安装路径下的 scripts 目录。     
  2. 通过Anaconda 安装扩展模块
  运行Anaconda ,打开Anaconda Navigator窗口,在主界面左边的导航栏中选择Environments选项,在右边下拉列表框中选择All选项,搜索并勾选相应的安装包,然后单击Apply按钮安装模块,如图3.2所示。

图3.2  通过Anaconda安装模块
3.3  案 例 精 选 
  【例3.11】 设计一个学生类。这个学生类中包含学生的学号、姓名和成绩。计算3名学生的平均成绩。
  
  程序代码如下:
  
     class Student:
     	 
     	def _ _init_ _(self, sid, name, scro):
     		self.sid = sid
     		self.name = name
     		self.scro = scro
     
     	def cot(self):
     		return self.scro
     	
     	def prnid(self):
     		print('学号:',self.sid, '姓名:',self.name, '成绩:',self.scro)
     
     
     stu1 = Student('a1001','张大山',92)
     stu2 = Student('a1002','李晓丽',82)
     stu3 = Student('a1003','赵志勇',97)
     
     stu1.prnid()
     stu2.prnid()
     stu3.prnid()
     s = stu1.cot() + stu2.cot() + stu3.cot()
     print('平均成绩:',int(s/3))
  
  程序运行结果如下:
  
     学号: a1001 姓名: 张大山 成绩: 92
     学号: a1002 姓名: 李晓丽 成绩: 82
     学号: a1003 姓名: 赵志勇 成绩: 97
     平均成绩: 90
  
  【例3.12】 设计一个学生类。这个学生类中包含学生的学号、姓名和成绩,并能根据学生人数计算平均成绩。 
  程序代码如下:
  
     class Student:
     
     	def _ _init_ _(self):
     		self.s = 0
     		self.count = 0
     
     	def cot(self):
     		return self.s/self.count
     
     	def prnid(self, data):
     		for i in data:
                 self.sid = i['sid']
                 self.name = i['name']
                 self.scro = i['scro']
                 self.s = self.s + self.scro
                 print('学号:',self.sid, '姓名:',self.name, '成绩:',self.scro)
                 self.count += 1
     
     data = [{'sid':'a1001','name':'张大山','scro':92},
             {'sid':'a1002','name':'李晓丽','scro':82},
             {'sid':'a1003','name':'赵志勇','scro':97}]
     stu = Student()
     stu.prnid(data)
     s = stu.cot() 
     print('平均成绩:',int(s))
  
  程序运行结果如下:
  
     学号: a1001 姓名: 张大山 成绩: 92
     学号: a1002 姓名: 李晓丽 成绩: 82
     学号: a1003 姓名: 赵志勇 成绩: 97
     平均成绩: 90
习  题  3
  1.编写一个具有加、减、乘、除功能的模块,然后通过导入另一个程序中调用。
  2.设计一个商品类,该类有商品编号、品名、价格、数量。应用该类,统计3种商品的总金额。