第4讲: Python面向对象(上)
课程链接:百度飞桨领航团零基础Python速成营
一、基础知识
1.1、变量
Python的变量类型
变量存储在内存中的值,这就意味着在创建变量时会在内存中创建一个地址。基于变量的数据类型,解释器会分配指定内存,并决定什么数据可以被存储在内存中。因此,变量可以指定不同的数据类型,这些变量可以存储整数,浮点数或字符串等。
Python 中的变量赋值不需要声明类型。每个变量在内存中创建,都包括变量的标识,名称和数据这些信息。每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建。等号=用来给变量赋值。等号=运算符左边是变量名,等号=运算符右边是存储在变量中的值。
Python中也允许同时对多变量进行赋值。
li1,li2,li3 = '170cm','172cm','173cm'
print(li1)
Python是一种动态类型的语言,变量的类型可以随时变化。使用内置函数type()可以返回变量类型。
#变量类型的变化
name = '飞桨' #字符串类型的变量
print(type(name))
#输出结果
name = 1024 #整型的变量
print(type(name))
#输出结果
允许多个变量指向同一个值。函数id()获取变量的内存地址。
no = number = 1024
id(no)
#结果为2678854174640
id(no)
#结果为2678854174640
1.2、变量的作用域
变量的作用域是指程序代码能够访问该变量的区域,如果超出该区域,再访问时就会出现错误,也就是说Python 中,程序的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量是在哪里赋值的,在函数运行之前或者运行之后所有的名字就都不存在了。变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称。
Python的作用域一共有4种,分别是:
Local:在程序的最内层,包含局部变量,比如,一个函数的内部。
Enclosing:包含了非局部(non-local)也非全局(non-global)的变量。比如,两个嵌套函数,函数(或类)A里面又包含了函数B,那么对于B中的名称来说 A中的作用域就为no-nlocal。
Global:当前脚本的最外层,比如,当前模块的全局变量。
Built-in: 包含了内建的变量/关键字等,比如,int。
(1)、局部变量
例如定义一个名称为f_demo的函数,在该函数内部定义一个变量message(称为局部变量),并为其赋值,然后输出该变量,最后在函数体外部再次输出message变量,程序则会出现异常。
情况2:在函数体内定义并且使用global关键字修饰后,该变量也就变为全局变量。在函数体外也可以访问到该变量,并且在函数体内还可以对其进行修改。
1.3、异常的处理
异常即是一个事件,该事件会在程序执行过程中发生,影响了程序的正常执行。一般情况下,在Python无法正常处理程序时就会发生一个异常。异常是Python对象,表示一个错误。当Python脚本发生异常时我们需要捕获处理它,否则程序会终止执行。
常见异常:
NameError
尝试访问一个没有声明的变量引发的错误
IndexError
索引超出序列范围引发的错误
IndentationError
缩进错误
ValueError
传入无效的参数
IoError
输入输出错误(如要读取的文件不存在)
ImportError
导入模块引发的错误
AttributeError
尝试访问未知的对象属性引发的错误
TypeError
类型不合适引发的错误
MemoryError
内存不足
ZeroDivisionError
除数为0引发的错误
捕捉异常可以使用try/except语句。try/except语句用来检测try语句块中的错误,从而让except语句捕获异常信息并处理。如果你不想在异常发生时结束你的程序,只需在try里捕获它。 如果当try后的语句执行时发生异常,python就跳回到try并执行第一个匹配该异常的except子句,异常处理完毕,控制流就通过整个try语句(除非在处理异常时又引发新的异常)。 如果在try后的语句里发生了异常,却没有匹配的except子句,异常将被递交到上层的try,或者到程序的最上层(这样将结束程序,并打印默认的出错信息)。 如果在try子句执行时没有发生异常,python将执行else语句后的语句(如果有else的话),然后控制流通过整个try语句。
1.4、常用函数
(1)、sorted函数
Python中sorted()函数可对所有可迭代的对象进行排序操作。
Tip:sort与sorted区别:
sort是应用在list上的方法,sorted可以对所有可迭代的对象进行排序操作。
list的sort方法返回的是对已经存在的列表进行操作,而内建函数sorted方法返回的是一个新的list,而不是在原来的基础上进行的操作。
(2)、pop函数
Python中pop()函数可用于移除列表中的一个元素(默认最后一个元素),并且返回该元素的值。
(3)、id函数
Python中id()函数可返回对象的唯一标识符,标识符是一个整数。Python 中id()可用于获取对象的内存地址。
(4)、dir函数
Python中dir()函数不带参数时,可返回当前范围内的变量、方法和定义的类型列表;带参数时,可返回参数的属性、方法列表。如果参数包含方法dir(),该方法将被调用。如果参数不包含dir(),该方法将最大限度地收集参数信息。
(5)、split函数
Python中split()函数可通过指定分隔符对字符串进行分割,如果参数num有指定值,则分隔num+1个子字符串。
(6)、help函数
Python中help()函数可用于查看函数或模块用途的详细说明。help()函数相当于一个内置的Python帮助手册,help()函数的使用可以让我们更快更清晰地理解Python里面新函数的作用。
1.5、模块
(1)、threading模块
threading,基于线程的并行,这个模块在较低级的模块 _thread (底层多线程 API)基础上建立较高级的线程接口。
该模块定义了以下函数:
threading.active_count() 返回当前存活的线程类 Thread 对象。返回的计数等于 enumerate() 返回的列表长度。
threading.current_thread() 返回当前对应调用者的控制线程的 Thread 对象。如果调用者的控制线程不是利用 threading 创建,会返回一个功能受限的虚拟线程对象。
threading.get_ident() 返回当前线程的 “线程标识符”。它是一个非零的整数。它的值没有直接含义,主要是用作 magic cookie,比如作为含有线程相关数据的字典的索引。线程标识符可能会在线程退出,新线程创建时被复用。
(2)、zipfile模块
ZIP 文件格式是一个常用的归档与压缩标准。 这个模块提供了创建、读取、写入、添加及列出 ZIP 文件的工具。 任何对此模块的进阶使用都将需要理解此格式,其定义参见 PKZIP 应用程序笔记。
此模块目前不能处理分卷 ZIP 文件。它可以处理使用 ZIP64 扩展(超过 4 GB 的 ZIP 文件)的 ZIP 文件。它支持解密 ZIP 归档中的加密文件,但是目前不能创建一个加密的文件。解密非常慢,因为它是使用原生 Python 而不是 C 实现的。
zipfile的几个常用函数:
ZipFile.close() 关闭归档文件。 你必须在退出程序之前调用 close() 否则将不会写入关键记录数据
ZipFile.getinfo(name) 返回一个 ZipInfo 对象,其中包含有关归档成员 name 的信息。 针对一个目前并不包含于归档中的名称调用 getinfo() 将会引发 KeyError。
ZipFile.infolist() 返回一个列表,其中包含每个归档成员的 ZipInfo 对象。 如果是打开一个现有归档则这些对象的排列顺序与它们对应条目在磁盘上的实际 ZIP 文件中的顺序一致。
ZipFile.namelist() 返回按名称排序的归档成员列表。
二、面向对象
1、输出一个选手的最好的三次成绩。
分析:读取文件——输出原始成绩——数据标准化——排序去重——输出三次最高成绩
选手James文件:james.txt 2-34,3:21,2,34,2.45,3.01,2:01,2:01,3:10,2-22
对于上图中get_coach_data函数的说明:
filename为文件路径
f表示文件对象
f.realine()表示读取文件的一行
line.strip().split(’,’)为链式函数写法意思是,先对这一行的数据进行strip(),就是去掉改行头尾空格和换行符。然后对strip()的结果进行split(’,’),对结果以逗号的进行切分形成一个数组。
对于上图中sanitize函数的说明:
time_string为时间数组
splitter是根据原数据的格式来确实分钟和秒的分隔符
(mins,secs) = time_string.split(splitter)以splitter分隔符切分每个时间数据到两个变量中mins,secs
return (mins+’.’+secs)将分钟和秒以字符串点进行连接
例:读取James成绩的完整代码
输出结果
2、如果有四位选手,怎么分别输出他们各自最好的三次成绩呢?
为数据增加了标识(在原来的数据前面增加了姓名和生日)。如
james.txt: james,2006-11-11,2-34,3:21,2.34,2.45,3.01,2:01,2:01,3:10,2-22
mikey.txt: mikey,2006-10-11,2-34,3:21,2.34,2.45,3.01,3:01,2:01,3:10,2-21
julie.txt: julie,2006-9-11,2-34,3:21,2.34,2.45,3.01,301,2:01,3:10,2-24
sarah.txt: sarah,2008-11-11,3-34,3:21,2.34,2.45,3.01,2:01,2:01,3:10,2-25
# 读取文件内容,按逗号进行切分
def get_coach_data(filename):
with open(filename) as f:
line = f.readline()
return line.strip().split(',')
# 数据格式标准化
def sanitize(time_string):
if '-' in time_string:
splitter = '-'
elif ':' in time_string:
splitter = ':'
else:
return (time_string)
(mins,secs) = time_string.split(splitter)
return (mins+'.'+secs)
# 读取新的数据文件
james_new = get_coach_data('james_new.txt')
mikey_new = get_coach_data('mikey_new.txt')
julie_new = get_coach_data('julie_new.txt')
sarah_new = get_coach_data('sarah_new.txt')
(james_name,james_dob) = james_new.pop(0),james_new.pop(0)
james_top3 = sorted(set([sanitize(t) for t in james_new]))[0:3]
print('姓名:%s,生日:%s,最快的3次成绩:%s' %(james_name,james_dob,james_top3))
(mikey_name,mikey_dob) = mikey_new.pop(0),mikey_new.pop(0)
mikey_top3 = sorted(set([sanitize(t) for t in mikey_new]))[0:3]
print('姓名:%s,生日:%s,最快的3次成绩:%s' %(mikey_name,mikey_dob,mikey_top3))
(julie_name,julie_dob) = julie_new.pop(0),julie_new.pop(0)
julie_top3 = sorted(set([sanitize(t) for t in julie_new]))[0:3]
print('姓名:%s,生日:%s,最快的3次成绩:%s' %(julie_name,julie_dob,julie_top3))
(sarah_name,sarah_dob) = sarah_new.pop(0),sarah_new.pop(0)
sarah_top3 = sorted(set([sanitize(t) for t in sarah_new]))[0:3]
print('姓名:%s,生日:%s,最快的3次成绩:%s' %(sarah_name,sarah_dob,sarah_top3))
3、上面的代码实在有点麻烦,找个简单的办法吧——字典。
james_new = get_coach_data('mywork/james_new.txt')
james_data={}
james_data['Name'] = james_new.pop(0)
james_data['Dob'] = james_new.pop(0)
james_data['top3'] = sorted(set([sanitize(t) for t in james_new]))[0:3]
print('姓名:%s,生日:%s,最快的3次成绩:%s' %(james_data['Name'],james_data['Dob'],james_data['top3']))
4、使用字典带来的问题——那就用”类“吧
即使有字典的帮助还不能完美的解决问题,函数与数据并没有直接联系,分散在源文件中,不利于代码的维护。那就使用类吧。
类的好处:
降低复杂性-》更少的bug-》提高可维护行
类可以将数据与函数绑定在一起,使代码模块化
调用数据和函数,使用对象名.的方式,使代码更加优雅
class Athlete:
def __init__(self,a_name,a_dob=None,a_times=[]):
self.name = a_name
self.dob = a_dob
self.times = a_times
def top3(self):
return sorted(set([sanitize(t) for t in self.times]))[0:3]
def sanitize(self,time_string):
if '-' in time_string:
splitter = '-'
elif ':' in time_string:
splitter = ':'
else:
return (time_string)
(mins,secs) = time_string.split(splitter)
return (mins+'.'+secs)
# 从文件中读取数据
james_new = get_coach_data('mywork/james_new.txt')
james_name = james_new.pop(0)
james_dob = james_new.pop(0)
james_times = james_new
# 创建Athlete对象
james = Athlete(james_name,james_dob,james_times)
print('姓名:%s,生日:%s,最快的3次成绩:%s' %(james.name,james.dob,james.top3()))
5、如何定义类
class Athlete
第一部分:class定义类的关键字,Athlete符合python标识符命名规则,:表示类内容的开始
def init(self,a_name,a_dob=None,a_times=[]):
第二部分:def定义函数的关键字,init 方法是一个特殊方法会在实例化对象时自动调用,我们会在这个方法中对数据进行赋值。self作为类中函数的第一个参数,方便该方法调用该类的其他属性和方法。
第三部分:自定义的属性和方法
6、如何使用类
(1)创建对象
对象名 = 类名(参数)
(2)使用:调用类的方法和属性
对象.属性名
对象.方法名()
7、类属性
所有对象共享的数据
如何使用:
定义:在 init之上,或者说在类的范围内与方法同等级别,书写变量名=值
8、类方法
所有对象共享的方法
如何使用:
定义:方法定义时,使用@classmethod标记
调用:
类名.类方法
对象.类方法
9、私有属性和方法
外部不能访问的属性和方法
如何使用:
定义:在属性和方法名前加 __ 两个下划线
调用:只能通过类中的方法来调用私有的属性和方法
三、练习作业
第1题
'james,2006-11-11,2-34,3:21,2.34,2.45,3.01,2:01,2:01,3:10,2-22'
存储以上的数据,如何定义运动员类,补全代码(4分)
class Athlete:
def __init__(self,a_name,a_dob=None,a_times=[]):
self.name=a_name
self.dob=a_dob
self.times=a_times
def top3(self):
return sorted(set([self.sanitize(t) for t in self.times]))[0:3]
def sanitize(self,time_string):
if '-' in time_string:
splitter = '-'
elif ':' in time_string:
splitter = ':'
else:
return (time_string)
(mins,secs) = time_string.split(splitter)
return (mins+'.'+secs)
第2题
数据所在文件的路径为'work/james_new.txt',使用运动员对象,补全代码(4分)
def get_coach_data(filename):
with open(filename) as f:
line = f.readline()
return line.strip().split(',')
#从文件中读取数据
james_new = get_coach_data('work/james_new.txt')
# 输出读取文件的结果
james_name = james_new.pop(0)
james_dob = james_new.pop(0)
james_times = james_new
#创建Athlete对象
class Athlete:
def __init__(self,a_name,a_dob=None,a_times=[]):
self.name=a_name
self.dob=a_dob
self.times=a_times
def top3(self):
return(sorted(set([self.sanitize(t)for t in self.times])))[0:3]
def sanitize(self,time_string):
if '-' in time_string:
splitter = '-'
elif ':' in time_string:
splitter = ':'
else:
return (time_string)
(mins,secs) = time_string.split(splitter)
return (mins+'.'+secs)
james = Athlete(james_name,james_dob,james_times)
print('姓名:%s,生日:%s,最快的3次成绩:%s' %(james.name,james.dob,james.top3()))
第3题
类属性,类方法,补全代码(4分)
class Athlete:
#运动员集训了,要买东西的同学要把地址改一下
#代码1
address = '中国足球协会训练基地xx街xx号'
def __init__(self,a_name,a_dob=None,a_times=[]):
self.name = a_name
self.dob = a_dob
self.times = a_times
def top3(self):
return sorted(set([self.sanitize(t) for t in self.times]))[0:3]
def sanitize(self,time_string):
if '-' in time_string:
splitter = '-'
elif ':' in time_string:
splitter = ':'
else:
return (time_string)
(mins,secs) = time_string.split(splitter)
return (mins+'.'+secs)
@classmethod
def changeAddress(cls):
cls.address = '中国田径训练基地xx街xx号'
Athlete.address = cls.address
new = Athlete('new',20000311,3.14)
print(new.address)
Athlete.changeAddress()
print(new.address)
第4题
将第3题中的实例变量name改为私有的属性,将sanitize改为私有方法,补全代码(4分)
class Athlete:
def __init__(self,a_name,a_dob=None,a_times=[]):
#代码1
self.__name = name
self.dob = a_dob
self.times = a_times
def sayName(self):
#代码2
print(self.__name)
def top3(self):
#代码3
return sorted(set([self.__sanitize(t) for t in self.times]))[0:3]
#代码4
def __sanitize(self,time_string):
if '-' in time_string:
splitter = '-'
elif ':' in time_string:
splitter = ':'
else:
return (time_string)
(mins,secs) = time_string.split(splitter)
return (mins+'.'+secs)
第5题
数据内容james,2006-11-11,2-34,3:21,2.34,2.45,3.01,2:01,2:01,3:10,2-22,以分钟.秒的形式打印'2-34'后面的所有时间。
输出的结果为'2.34', '3.21', '2.34', '2.45', '3.01', '2.01', '2.01', '3.10', '2.22',补全代码。(4分)
def sanitize(time_string):
#代码1
if '-' in time_string:
splitter = '-'
elif ':' in time_string:
splitter = ':'
else:
return (time_string)
(mins,secs) = time_string.split(splitter)
return (mins+'.'+secs)
#代码2
james = data.strip().split(',')
name = james.pop(0)
dob = james.pop(0)
#代码3
times = [sanitize(t) for t in james]
print(times)
学习得好认真,加油!