集合和函数
一、集合
1.1 集合的特征
集合是无序的,集合中的元素是唯一的,集合一般用于元组或者列表中的元素去重
1.2 集合的格式
格式
变量名=set()
变量名.add(元素)
# 必须不能初始化值
或
变量名={元素,元素,,,}
注意:下面写法为一个空字典,为空默认是字典,如果有数据在根据格式判断为字典还是集合
name={}
1.3 添加元素
1.3.1 add
- 案例
nums = {11,24,45,96,28}
nums.add(42)
print(nums)
#{96, 42, 11, 45, 24, 28}
1.3.2 update
- 案例
nums = {11,24,45,96,28}
nums2=["anfly","tom"]
nums.update(nums2)
print(nums)
#{96, 'anfly', 11, 45, 24, 28, 'tom'}
1.4:删除元素
remove,pop,discard
1.4.1 remove
使用remove删除集合中的元素 如果有直接删除 如果没有程序报错
- 案例:该元素存在
nums = {11,24,45,96,28}
nums.remove(24)
print(nums)
#{96, 11, 45, 28}
- 案例:该元素不存在
nums = {11,24,45,96,28}
nums.remove(245)
print(nums)
#KeyError: 245
1.4.2 pop
1、如果集合的元素都是数字, 删除时, 删掉的是最小的数字, 其余数字升序排列
2、如果集合的元素是非数字, 删除时, 删掉的是随机的元素, 其余元素随机排列
3、如果集合里既有数字又有非数字元素, 删除时:
若删掉的是数字, 则一定是删掉了最小的, 其他数字升序排列, 非数字元素随机排列;
若删掉的非数字, 则一定是随机删掉了一个, 其他数字升序排列, 非数字则随机排列.
如果集合没有元素程序报错
- 案例:有元素
nums = {11,24,45,96,28}
nums.pop()
print(nums)
#{11, 45, 24, 28}
- 案例:无元素
nums = {}
nums.pop()
print(nums)
#TypeError: pop expected at least 1 arguments, got 0
1.4.3 discard
使用discard删除 如果元素存在直接删除 如果元素不存在不做任何操作
- 案例:该元素存在
nums = {11,24,45,96,28}
nums.discard(24)
print(nums)
#{96, 11, 45, 28}
- 案例:该元素不存在
nums = {11,24,45,96,28}
nums.discard(242)
print(nums)
#{96, 11, 45, 24, 28}
1.5:集合的交集和并集
1.5.1:交集
使用&连接多个集合,得到相同的元素
- 案例:存在相同元素
set1 = {"anfly","tom","haha"}
set2 = {"anfly","susala"}
set3= set1 & set2
print(set3)
返回值:{'anfly'}
- 案例:存在相同元素
set1 = {"anfly","tom","haha"}
set2 = {"anfly2","susala"}
set3= set1 & set2
print(set3)
返回值:set()
1.5.2:并集
使用|连接多个集合,得到全部集合中全部的元素
- 案例:
set1 = {1, 2, 3, 4}
set2 = {3, 4, 5, 6}
new_set = set1 | set2
print(new_set)
#{1, 2, 3, 4, 5, 6}
1.6 公共方法
1.6.1:运算符
(1)长度:len()
(2)最大值:max()
(3)删除:del()
二、函数
2.1 什么是函数
在开发程序时,需要某块代码多次,但是为了提高编写的效率以及代码的重用,所以把具有独立功能的代码块组织为一个小模块,这就是函数
2.2 函数定义和调用
格式
def 函数名():
执行语句
函数名() #调用函数
案例
def hello():
print("hello word")
hello()
注意:
定义了函数之后,就相当于有了一个具有某些功能的代码,想要让这些代码能够执行,需要调用它
调用函数很简单的,通过 函数名() 即可完成调用
每次调用函数时,函数都会从头开始执行,当这个函数中的代码执行完毕后,意味着调用结束了
当然了如果函数中执行到了return也会结束函数
2.3 函数参数
思考:现在需要定义一个函数,这个函数能够完成2个数的加法运算,并且把结果打印出来,该怎样设计?
案例
def add2num():
a = 11
b = 22
c = a+b
print c
思考:如果为了让一个函数更通用,即想让它计算哪两个数的和,就让它计算哪两个数的和,在定义函数的时候可以让函数接收数据?
引入:参数传递
2.3.1:位置参数
格式
def 函数名(参数1,参数2):
代码块
函数名(值1,值2)
案例:
def fun(a,b):
print("a:",a)
print("b:",b)
fun(2,3)
结果:a: 2 b:3
小总结
定义时小括号中的参数,用来接收参数用的,称为 “形参”
调用时小括号中的参数,用来传递给函数用的,称为 “实参”
2.3.2 关键字参数
格式
def 函数名(参数1,参数2):
代码块
函数名(参数1=值1,参数2=值2)
案例:
def fun(a,b):
print("a:",a)
print("b:",b)
fun(a=2,b=3)
结果:a: 2 b:3
注意:参数调用时,可以改变传参顺序,如果有位置参数需要位置参数放在关键字参数前面
案例:
def fun(a,b):
print("a:",a)
print("b:",b)
fun(3,b=2)
结果:a: 3 b:2
如果关键字参数传参要在位置参数之前,将会报错
案例:错误
def fun(a,b):
print("a:",a)
print("b:",b)
fun(a = 3,2)
2.3.3:缺省参数
在形参中默认有值的参数,称之为缺省参数
案例:调用函数时,缺省参数的值没有传入
def printinfo(name,age=20):
print("name:",name)
print("age:",age)
printinfo(name="anfly")
#name: anfly
#age: 20
案例:调用函数时,缺省参数的值传入
def printinfo(name,age=20):
print("name:",name)
print("age:",age)
printinfo(name="anfly",age=10)
#name: anfly
#age: 10
案例
def printinfo(age=20,name):
print("name:",name)
print("age:",age)
printinfo(name="anfly",age=10)
总结
调用函数时,缺省参数的值如果没有传入,则取默认值(形式参数),如果传入,则取实际参数
缺省参数一定要位于位置参数的最后面
2.3.4 不定长参数
有时可能需要一个函数能处理比当初声明时更多的参数, 这些参数叫做不定长参数,声明时不会命名。
格式
def printinfo(*args,**kwargs):
print("args:",args)
print("kwargs:",kwargs)
printinfo(参数)
注意:加了星号()的变量args会存放所有未命名的变量参数,args为元组
而加*的变量kwargs会存放命名参数,即形如key=value的参数, kwargs为字典
案例:不定长参数* args
def printinfo(*args):
print("args:",args)
printinfo(100,200,300,400)
#args: (100, 200, 300, 400)
案例:* args不能接收key=value类型的参数
def printinfo(*args):
print("args:",args)
printinfo(100,200,300,b = 400)
案例:不定长参数* * kwargs
def printinfo(**kwargs):
print("kwargs:",kwargs)
printinfo(a=100,b=200,c=300,d= 400)
#kwargs: {'a': 100, 'b': 200, 'c': 300, 'd': 400}
案例:* * kwargs不能接收未命名的变量参数
def printinfo(**kwargs):
print("kwargs:",kwargs)
printinfo(100,b=200,c=300,d= 400)
2.3.5:参数位置顺序
格式
def fun(位置参数,args,缺省参数,*kwargs):
代码块
fun(参数值)
案例
def sun(a,*args,b=22,**kwargs):
print("a:",a)
print("args:",args)
print("b:",b)
print("kwargs:",kwargs)
sun(100,200,300,b=2,m=3,n=4)
返回值:
a: 100
args: (200, 300)
b: 2
kwargs: {'m': 3, 'n': 4}
注意:•如果很多个值都是不定长参数,那么这种情况下,可以将缺省参数放到 args的后面, 但如果有kwargs的话,*kwargs必须是最后的
2.4:函数返回值
场景:•我给儿子10块钱,让他给我买包烟。这个例子中,10块钱是我给儿子的,就相当于调用函数时传递到参数,让儿子买烟这个事情最终的目标是,让他把烟给你带回来然后给你对么,,,此时烟就是返回值
格式
def sum():
代码块
return 值
sum()
代码
def sum(a,b):
return a+b
sum(1,2)
代码:查看返回值
def sum(a,b):
return a+b
result = sum(1,2) #保存函数的返回值
print(result)
2.4.1:多个return
def create_nums(num):
print("---1---")
if num == 100:
print("---2---")
return num+1 # 函数中下面的代码不会被执行,因为return除了能够将数据返回之外,还有一个隐藏的功能:结束函数
print("return执行之后不会继续执行")
print(“1231223")
else:
print("---3---")
return num+2
print("---4---")
result1 = create_nums(100)
print(result1) # 打印101
result2 = create_nums(200)
print(result2) # 打印202
注意:一个函数中可以有多个return语句,但是只要有一个return语句被执行到,那么这个函数就会结束了,因此后面的return没有什么用处
2.4.2:返回多个数据
代码
def divid(a, b):
shang = a//b #取模
yushu = a%b #取余
return shang, yushu #默认是元组
result = divid(5, 2)
print(result)
# 输出(2, 1)
总结:return后面可以是元组,列表、字典等,只要是能够存储多个数据的类型,就可以一次性返回多个数据
2.5:函数类型
分类:
- 无参数,无返回值
- 无参数,有返回值
- 有参数,无返回值
- 有参数,有返回值
2.5.1:无参数,无返回值的函数
此类函数,不能接收参数,也没有返回值,一般情况下,打印提示灯类似的功能,使用这类的函数
def printMenu():
print('--------------------------')
print(' xx涮涮锅 点菜系统')
print('')
print(' 1. 羊肉涮涮锅')
print(' 2. 牛肉涮涮锅')
print(' 3. 猪肉涮涮锅')
print('--------------------------')
2.5.2:无参数,有返回值的函数
•此类函数,不能接收参数,但是可以返回某个数据,一般情况下,像采集数据,用此类函数
def getTemperature():
# 这里是获取温度的一些处理过程
# 为了简单起见,先模拟返回一个数据
return 24
2.5.3:有参数,无返回值的函数
•此类函数,能接收参数,但不可以返回数据,一般情况下,对某些变量设置数据而不需结果时,用此类函数
2.5.4:有参数,有返回值的函数
•此类函数,不仅能接收参数,还可以返回某个数据,一般情况下,像数据处理并需要结果的应用,用此类函数
计算1~num的累积和(案例需实际演示)
def calculateNum(num):
result = 0
i = 1
while i<=num:
result = result + i
i+=1
return result
2.6:函数的嵌套
一个函数里面又调用了另外一个函数,这就是所谓的函数嵌套调用
案例
def testb():
print("testb start")
print("testb testb 执行")
print("testb end")
def testa():
print("testa start")
testb()
print("testa end")
返回值
testa start
testb start
testb testb 执行
testb end
testa end
注意:如果函数A中,调用了另外一个函数B,那么先把函数B中的任务都执行完毕之后才会回到上次 函数A执行的位置
课堂作业
写一个函数求三个数的和,并返回结果
写一个函数求三个数的平均值,并返回结果
再写一个函数求每个数与平均值之间的差,并返回结果
写一个函数打印一条横线
打印自定义行数的横线
打印一条横线
def printOneLine():
print("-"*30)
# 打印多条横线
def printNumLine(num):
i=0
# 因为printOneLine函数已经完成了打印横线的功能,
# 只需要多次调用此函数即可
while i<num:
printOneLine()
i+=1
printNumLine(3)
2.8:匿名函数
lambda函数也叫匿名函数,即函数没有具体的名称
代码
g = lambda x :x+1
print(g(1))
def g(x):
return x + 1
print(g(1))
注意:lambda函数可以赋值给变量,默认是返回的,所以不用再加return关键字
注释:例如g = lambda x:x+1,可以看成如下函数,冒号前是参数,可以有多个,用逗号隔开,冒号右边的返回值
三、变量
3.1:局部变量
定义
局部变量,就是在函数内部定义的变量
约束
其作用范围是这个函数内部,即只能在这个函数中使用,在函数的外部是不能使用的
代码
def test1():
a = 120
print("我是局部变量a:",a)
a = 240
print("修改之后的局部变量a:",a)
def test2():
a =360
print("我是test02函数中的局部变量a",a)
test1()
test2()
返回值
我是局部变量a: 120
修改之后的局部变量a: 240
我是test02函数中的局部变量a 360
注意:局部变量的作用,为了临时保存数据需要在函数中定义变量来进行存储
当函数调用时,局部变量被创建,当函数调用完成后这个变量就不能够使用了
3.2:全局变量
定义
在函数外边定义的变量叫做全局变量
约束
全局变量能够在所有的函数中进行访问
代码
a = 100
def test1():
print(a) # 虽然没有定义变量a但是依然可以获取其数据
def test2():
print(a) # 虽然没有定义变量a但是依然可以获取其数据
# 调用函数
test1()
test2()
返回值: 100 100
3.2.1 全局变量和局部变量名字相同
代码
a = 100 #全局变量
def test1():
a= 300
print("修改后的a:",a)
def test2():
print(a)
# 调用函数
test1()
test2()
返回值: 修改后的a:300 100
注意:函数中的变量修改不会导致全局变量改变
3.2.2:全局变量的修改
代码:global的使用
a = 100 #全局变量
def test1():
global a #将a变成全局变量
a= 300
print("修改后的a",a)
def test2():
print(a)
# 调用函数
test1()
test2()
注意:在函数中出现global 全局变量的名字 那么这个函数中即使出现和全局变量名相同的变量名 = 数据 也理解为对全局变量进行修改,而不是定义局部变量
扩展
在一个函数中需要对多个全局变量进行修改,那么可以使用
global a, b
还可以用多次global声明都是可以的
global a
global b
文件的作用
使用文件的目的:保存数据存放在磁盘
把一些存储存放起来,可以让程序下一次执行的时候直接使用,而不必重新制作一份,省时省力
二、文件的打开与关闭
思考:如果想用word编写一份简历,应该有哪些流程呢?
步骤:新建--写入数据--关闭
打开word软件,新建一个word文件
写入个人简历信息
保存文件
关闭word软件
同样,在操作文件的整体过程与使用word编写一份简历的过程是很相似的
结论
打开文件,或者新建立一个文件
读/写数据
关闭文件
2.1 打开文件
在python,使用open函数,可以打开一个已经存在的文件,或者创建一个新文件open(文件名,访问模式)
格式
f = open(‘文件’, 'w')或者f = open('文件', 'r'
常见的文件操作有:写,读,追加
2.1.1 写数据(write)
- 格式
对象 = open("文件",w)
对象.write("写入数据")
对象.close
- 案例:以写的方式打开文件,写入数据
f = open('test.txt', 'w')
f.write('hello world, i am here!')
f.close()
- 继续写入数据
f = open('test.txt', 'w')
f.write('I love you')
f.close()
- 总结:
如果文件不存在那么创建,如果存在那么就先清空,然后写入数据
2.1.2 读数据(read)
- 案例:以读的方式打开文件,读取数据
- 格式
对象 = open("文件",r)
变量 = 对象.read()
print(变量)
- 案例:读取文件(test.txt)
f = open('test.txt', 'r')
content = f.read()
print(content)
- 总结:
如果用open打开文件时,如果使用的"r",那么可以省略,即只写 open('test.txt')
如果没有文件,打开报错,存在该文件才能操作
如果文件中存在中文显示,会出现乱码需要添加encoding='utf-8'
open(‘test.txt’,”r”, encoding='utf-8')
- 思考:如果只想读取几个字怎么操作?
- 案例:读取指定长度的数据(单位是字节)
f = open('test.txt', 'r')
content = f.read(5) # 最多读取5个数据
print(content)
注意:
使用read(num)可以从文件中读取数据,num表示要从文件中读取的数据的长度(单位是字节),如果没有传入num,那么就表示读取文件中所有的数据
2.1.3 读数据(readlines)
就像read没有参数时一样,readlines可以按照行的方式把整个文件中的内容进行一次性读取,并且返回的是一个列表,其中每一行的数据为一个元素
- 格式
对象 = open("文件",r)
变量 = 对象.readlines()
print(变量)
- 案例:读取文件(test.txt)
f = open('test.txt', 'r')
content = f.readlines()
print(content)
print(type(content))
2.1.4 读数据(readline)
readline可以按照行的方式把整个文件中的内容进行一行一行的读取
- 格式
对象 = open("文件",r)
变量 = 对象.readline()
print(变量)
- 案例,一行一行读取
f = open('a.txt', 'r', encoding='utf-8')
while True:
content = f.readline()
if content:
print(content)
else:
break
2.1.5 可写(a)
- 格式
对象 = open("",a)
对象.write("写入数据")
对象.close
- 案例,在文件中写入数据
f = open("test.txt",a)
f.write("新的数据")
对象.close
- 总结:
(可写)形式打开文件,如果文件不存在则创建并从头添加写入的内容,存在则原有数据后追加数据
2.1.6 读数据(rb)
- 格式1
对象 = open("二进制文件",rb)
变量= 对象.read()
print(变量)
- 格式2
with open("二进制文件","rb") as 对象:
变量 = 对象.read()
print(变量)
- 案例:使用尽可能多的方式实现读取图片
f = open('33.jpg', 'rb')
content = f.read()
print(content)
with open("33.jpg","rb") as rf:
res = rf.read()
print(res)
使用习惯:格式2中不需要手动关闭文件,所以经常被使用
- 总结
如果没有文件,打开报错,存在该文件才能操作
2.1.7 写数据(wb)
- 格式
with open("二进制文件","wb") as 对象:
变量 = 对象.write()
print(变量)
- 案例:备份图片
with open("1.jpg","rb") as rf:
res = rf.read()
with open("textjpg.jpg", "wb") as wf:
res = wf.write(res)
print(res)
2.2 关闭文件
- 格式
close( )
2.3 思考题
如果一个文件很大,比如5G,试想应该怎样把文件的数据读取到内存然后进行处理呢?
调用read()会一次性读取文件的全部内容,如果文件有10G,内存就爆了,所以,要保险起见,可以反复调用read(size)方法�每次最多读取size个字节的内容。另外,调用readline()可以每次读取一行内容,调用readlines()一次读取所有内容并按行返回list�因此,要根据需要决定怎么调用。如果文件很小,read()一次性读取最方便;如果不能确定文件大小,反复调用read(size)比较保险;如果是配置文件,调用readlines()最方便
三、文件和文件夹的操作
文件的相关操作
有些时候,需要对文件进行重命名、删除等一些操作,python的os模块中都有这么功能
3.1 文件重命名
os模块中的rename()可以完成对文件的重命名操作
- 格式
import os
os.rename("需要修改的文件名","新文件名")
- 案例:
import os
os.rename("test.txt","new.txt")
3.2 删除文件
os模块中的remove()可以完成对文件的重命名操作
- 格式
import os
os.remove("文件名")
- 案例:
import os
os.rename("new.txt")
文件夹的相关操作
2.1 创建文件夹
os模块中的mkdir()可以完成对文件的重命名操作
2.2 获取当前目录
os模块中的getcwd()可以获取当前目录
四、异常
1:什么是异常?
异常即是一个事件,该事件会在程序执行过程中发生,影响了程序的正常执行。
一般情况下,在Python无法正常处理程序时就会发生一个异常。
异常是Python对象,表示一个错误。
当Python脚本发生异常时我们需要捕获处理它,否则程序会终止执行。
- 演示异常
f = open("123.txt","r") #FileNotFoundError: [Errno 2] No such file or directory: '123.txt'
list =[1,2,3]
print(list[4]) #IndexError: list index out of range
str="jack"
str.index("v") ValueError: substring not found
c= 5/0
print(c) #ZeroDivisionError: division by zero
2 异常处理
2.1 作用
捕捉异常可以使用try/except语句。
try/except语句用来检测try语句块中的错误,从而让except语句捕获异常信息并处理。
如果你不想在异常发生时结束你的程序,只需在try里捕获它。
2.2 语法
以下为简单的try....except...else的语法
try:
<语句> #运行别的代码
except <名字>:
<语句> #如果在try部份引发了'name'异常
else:
<语句> #如果没有异常发生
- 代码
try:
open("qwe.txt","r")
print("123")
except FileNotFoundError:
print("异常处理")
else:
print("没有异常")
try:
open("qwe.txt","r")
print("123")
except FileNotFoundError as result:
print("异常处理",result)
else:
print("没有异常")
2.3:使用except而不带任何异常类型
你可以不带任何异常类型使用except,如下实例
- 语法
try:
正常的操作
except :
发生异常,执行这块代码
else:
如果没有异常执行这块代码
- 代码
try:
open("qwe.txt","r")
print("123")
except :
print("异常处理")
else:
print("没有异常")
注意:以上方式try-except语句捕获所有发生的异常。但这不是一个很好的方式,我们不能通过该程序识别出具体的异常信息。因为它捕获所有的异常。
2.4:使用except而带多种异常类型
- 语法
try:
异常的操作
except(Exception1[, Exception2[,...ExceptionN]]]):
发生以上多个异常中的一个,执行这块代码
......................
else:
如果没有异常执行这块代码
- 代码
list = [1,2,3,4]
try:
open("qwe.txt", "r")
list[7]
except (NameError,FileNotFoundError) as rese:
print("出现异常")
else:
print("没有异常")
2.5:try-finally 语句
try-finally 语句无论是否发生异常都将执行最后的代码
- 语法
try:
<语句>
finally:
<语句> #退出try时总会执行
- 案例
try:
fh = open("test.txt", "r")
fh.readlines()
fh.close()
finally:
print("Error: 没有找到文件或读取文件失败")
注意:当在try块中抛出一个异常,立即执行finally块代码。
finally块中的所有语句执行后,异常被再次触发,并执行except块代码。
参数的内容不同于异常
2.6 异常的传递
- 代码
def func1():
print("---func1--1---")
print(num)
print("---func1--2---")
# def func2():
# print("--func2--1---")
# func1()
# print("--func2--2---")
def func3():
try:
print("---func3--1---")
func1()
print("--func3--2----")
except Exception as result:
print(result)
print("--func3---3---")
func3()
#func2()
2.7:触发异常
可以使用raise语句自己触发异常
- 案例:输入考生的成绩(0~100)
def functionName( score ):
if score < 0 or score >100:
raise Exception("Invalid score!", score)
# 触发异常后,后面的代码就不会再执行
functionName(200)
2.8:用户自定义异常
- 定义
通过创建一个新的异常类,程序可以命名它们自己的异常。异常应该是典型的继承自Exception类,通过直接或间接的方式 - 代码:长度不低于3为
class ShortInputException(Exception):
def __init__(self, length, atleast):
self.length = length
self.atleast = atleast
def main():
try:
s = input('请输入 --> ')
if len(s) < 3:
# raise引发一个你定义的异常
raise ShortInputException(len(s), 3)
except ShortInputException as result:#x这个变量被绑定到了错误的实例
print('ShortInputException: 输入的长度是 %d,长度至少应是 %d'% (result.length, result.atleast))
else:
print('没有异常发生')
main()
五、模块
Python 模块(Module),是一个Python文件,以.py 结尾,包含了Python 对象定义和Python语句
- 下例是个简单的模块 aa.py:
def test1():
print("我是模块1")
def test2():
print("我是模块2")
5.1:模块的引入
5.1.1:import
模块定义好后,我们可以使用 import 语句来引入模块,语法如下
import module1[, module2[,... moduleN]]
test.py 文件代码
import aa
aa.test1()
aa.test2()
#我是模块1
#我是模块2
导入python中的模块os/math
- 代码:开平方根
import math
print(math.sqrt(4))
#2
注意:一个模块只会被导入一次,不管你执行了多少次。这样可以防止导入模块被一遍又一遍地执行。
5.1.2:from…import 语句
Python 的 from 语句让你从模块中导入一个指定的部分到当前命名空间中,语法如下
from modname import name1[, name2[, ... nameN]]
test.py 文件代码
from aa import test1
test1()
#我是模块1
注意:只能调用导入的模块中的部分,如果想使用test2,需要如下操作
from aa import test1,test2
test1()
test2()
#我是模块1
#我是模块2
- 思考:如果需要导入模块中的部分有很多,怎么操作?
5.1.3:from…import * 语句
把一个模块的所有内容全都导入到当前的命名空间也是可行的,只需使用如下声明:
from modname import *
test.py 文件代码
from aa import *
test1()
test2()
#我是模块1
#我是模块2
- 扩展
使用__all__魔幻方法
当被引入的包中有__all__=(),里面指定的才能被调用,调用的时候使用from模块名 import*
aa.py
__all__=('test1','test2')
def test1():
print("我是模块1")
def test2():
print("我是模块2")
def test3():
print("我是模块2")
test.py
from aa import *
test1()
test2()
六、Python中的包
包是一个分层次的文件目录结构,它定义了一个由模块及子包,和子包下的子包等组成的 Python 的应用环境。
简单来说,包就是文件夹,但该文件夹下必须存在 init.py 文件, 该文件的内容可以为空。init.py 用于标识当前文件夹是一个包。
考虑一个在 day 目录下的 runoob1.py、runoob2.py、init.py 文件,test.py 为测试调用包的代码,目录结构如下
test.py
day
|-- __init__.py
|-- runoob1.py
|-- runoob2.py
- 源代码如下:
day/runoob1.py
def runoob1():
print "I'm in runoob1"
day/runoob2.py
def runoob2():
print "I'm in runoob2"
然后我们在 day 同级目录下创建 test.py 来调用 day 包
# 导入 Phone 包
from day.runoob1 import runoob1
from day.runoob2 import runoob2
runoob1()
runoob2()
- 结果
I'm in runoob1
I'm in runoob2
Python 面向对象
[图片上传失败...(image-65895e-1648367148452)]
Anwfly[图片上传失败...(image-c3313f-1648366937613)] 已关注
<time datetime="2021-04-13T12:55:33.000Z" style="box-sizing: border-box; margin-right: 10px;">2021.04.13 20:55:33</time>字数 2,862阅读 431
<article class="_2rhmJa" style="box-sizing: border-box; display: block; font-weight: 400; line-height: 1.8; margin-bottom: 20px; word-break: break-word; position: relative; color: rgb(64, 64, 64); font-family: -apple-system, BlinkMacSystemFont, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">
Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是很容易的。本章节我们将详细介绍Python的面向对象编程。
如果你以前没有接触过面向对象的编程语言,那你可能需要先了解一些面向对象语言的一些基本特征,在头脑里头形成一个基本的面向对象的概念,这样有助于你更容易的学习Python的面向对象编程。
接下来我们先来简单的了解下面向对象的一些基本特征。
面向对象技术简介
- 类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
- 类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
- 数据成员:类变量或者实例变量, 用于处理类及其实例对象的相关的数据。
- 方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
- 局部变量:定义在方法中的变量,只作用于当前实例的类。
- 实例变量:在类的声明中,属性是用变量来表示的。这种变量就称为实例变量,是在类声明的内部但是在类的其他成员方法之外声明的。
- 继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例图,Dog是一个Animal)。
- 实例化:创建一个类的实例,类的具体对象。
- 方法:类中定义的函数。
- 对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
创建类
使用 class 语句来创建一个新类,class 之后为类的名称并以冒号结尾:
class ClassName:
'类的帮助信息' #类文档字符串
class_suite #类体
类的帮助信息可以通过ClassName.doc查看。
class_suite 由类成员,方法,数据属性组成。
实例
以下是一个简单的 Python 类的例子:
实例
# !/usr/bin/python # -*- coding: UTF-8 -*-
class Employee:
'所有员工的基类'
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def displayCount(self):
print("Total Employee %d" % Employee.empCount)
def displayEmployee(self):
print("Name : ", self.name, ", Salary: ", self.salary)
- empCount 变量是一个类变量,它的值将在这个类的所有实例之间共享。你可以在内部类或外部类使用 Employee.empCount 访问。
- 第一种方法init()方法是一种特殊的方法,被称为类的构造函数或初始化方法,当创建了这个类的实例时就会调用该方法
- self 代表类的实例,self 在定义类的方法时是必须有的,虽然在调用时不必传入相应的参数。
self代表类的实例,而非类
类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self。
class Test:
def prt(self):
print(self)
print(self.__class__)
t = Test()
t.prt()
以上实例执行结果为:
<__main__.Test instance at 0x10d066878>
__main__.Test
从执行结果可以很明显的看出,self 代表的是类的实例,代表当前对象的地址,而 self.class**** 则指向类。
self 不是 python 关键字,我们把他换成 runoob 也是可以正常执行的:
实例
class Test:
def prt(runoob):
print(runoob)
print(runoob.__class__)
t = Test()
t.prt()
以上实例执行结果为:
<__main__.Test instance at 0x10d066878>
__main__.Test
创建实例对象
实例化类其他编程语言中一般用关键字 new,但是在 Python 中并没有这个关键字,类的实例化类似函数调用方式。
以下使用类的名称 Employee 来实例化,并通过 init 方法接收参数。
"创建 Employee 类的第一个对象"
emp1 = Employee("Zara", 2000)
"创建 Employee 类的第二个对象"
emp2 = Employee("Manni", 5000)
访问属性
您可以使用点号 . 来访问对象的属性。使用如下类的名称访问类变量:
emp1.displayEmployee()
emp2.displayEmployee()
print "Total Employee %d" % Employee.empCount
完整实例:
实例
#!/usr/bin/python
# -*- coding: UTF-8 -*-
class Employee:
'所有员工的基类'
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def displayCount(self):
print("Total Employee %d" % Employee.empCount)
def displayEmployee(self):
print("Name : ", self.name, ", Salary: ", self.salary)
"创建 Employee 类的第一个对象"
emp1 = Employee("Zara", 2000)
"创建 Employee 类的第二个对象"
emp2 = Employee("Manni", 5000)
emp1.displayEmployee()
emp2.displayEmployee()
print("Total Employee %d" % Employee.empCount)
执行以上代码输出结果如下:
Name : Zara ,Salary: 2000
Name : Manni ,Salary: 5000
Total Employee 2
你可以添加,删除,修改类的属性,如下所示:
emp1.age = 7 # 添加一个 'age' 属性
emp1.age = 8 # 修改 'age' 属性
del emp1.age # 删除 'age' 属性
你也可以使用以下函数的方式来访问属性:
- getattr(obj, name[, default]) : 访问对象的属性。
- hasattr(obj,name) : 检查是否存在一个属性。
- setattr(obj,name,value) : 设置一个属性。如果属性不存在,会创建一个新属性。
- delattr(obj, name) : 删除属性。
hasattr(emp1, 'age') # 如果存在 'age' 属性返回 True。
getattr(emp1, 'age') # 返回 'age' 属性的值
setattr(emp1, 'age', 8) # 添加属性 'age' 值为 8
delattr(emp1, 'age') # 删除属性 'age'
Python内置类属性
- dict : 类的属性(包含一个字典,由类的数据属性组成)
- doc :类的文档字符串
- name: 类名
- module: 类定义所在的模块(类的全名是'main.className',如果类位于一个导入模块mymod中,那么className.module 等于 mymod)
-
bases : 类的所有父类构成元素(包含了一个由所有父类组成的元组)
Python内置类属性调用实例如下:
实例
#!/usr/bin/python
# -- coding: UTF-8 --
class Employee:
'所有员工的基类'
empCount = 0
def init(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def displayCount(self):
print("Total Employee % d" % Employee.empCount)
def displayEmployee(self):
print("Name : ", self.name, ", Salary: ", self.salary)
print("Employee.doc:", Employee.__doc__)
print("Employee.name:", Employee.__name__)
print("Employee.module:", Employee.__module__)
print("Employee.bases:", Employee.__bases__)
print("Employee.dict:", Employee.__dict__)
执行以上代码输出结果如下:
Employee.__doc__: 所有员工的基类
Employee.__name__: Employee
Employee.__module__: __main__
Employee.__bases__: ()
Employee.__dict__: {'__module__': '__main__', 'displayCount': <function displayCount at 0x10a939c80>, 'empCount': 0, 'displayEmployee': <function displayEmployee at 0x10a93caa0>, '__doc__': '\xe6\x89\x80\xe6\x9c\x89\xe5\x91\x98\xe5\xb7\xa5\xe7\x9a\x84\xe5\x9f\xba\xe7\xb1\xbb', '__init__': <function __init__ at 0x10a939578>}
python对象销毁(垃圾回收)
Python 使用了引用计数这一简单技术来跟踪和回收垃圾。
在 Python 内部记录着所有使用中的对象各有多少引用。
一个内部跟踪变量,称为一个引用计数器。
当对象被创建时, 就创建了一个引用计数, 当这个对象不再需要时, 也就是说, 这个对象的引用计数变为0 时, 它被垃圾回收。但是回收不是"立即"的, 由解释器在适当的时机,将垃圾对象占用的内存空间回收。
a = 40 # 创建对象 <40>
b = a # 增加引用, <40> 的计数
c = [b] # 增加引用. <40> 的计数
del a # 减少引用 <40> 的计数
b = 100 # 减少引用 <40> 的计数
c[0] = -1 # 减少引用 <40> 的计数
垃圾回收机制不仅针对引用计数为0的对象,同样也可以处理循环引用的情况。循环引用指的是,两个对象相互引用,但是没有其他变量引用他们。这种情况下,仅使用引用计数是不够的。Python 的垃圾收集器实际上是一个引用计数器和一个循环垃圾收集器。作为引用计数的补充, 垃圾收集器也会留心被分配的总量很大(即未通过引用计数销毁的那些)的对象。 在这种情况下, 解释器会暂停下来, 试图清理所有未引用的循环。
实例
析构函数 del ,del在对象销毁的时候被调用,当对象不再被使用时,del方法运行:
#!/usr/bin/python
# -- coding: UTF-8 --
class Point:
def init(self, x=0, y=0):
self.x = x
self.y = y
def __del__(self):
class_name = self.__class__.__name__
print(class_name, "销毁")
pt1 = Point()
pt2 = pt1
pt3 = pt1
print
id(pt1), id(pt2), id(pt3) # 打印对象的id
del pt1
del pt2
del pt3
以上实例运行结果如下:
3083401324 3083401324 3083401324
Point 销毁
注意:通常你需要在单独的文件中定义一个类,
类的继承
面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。
通过继承创建的新类称为子类或派生类,被继承的类称为基类、父类或超类。
继承语法
class 派生类名(基类名)
...
在python中继承中的一些特点:
- 1、如果在子类中需要父类的构造方法就需要显式的调用父类的构造方法,或者不重写父类的构造方法。详细说明可查看: python 子类继承父类构造函数说明。
- 2、在调用基类的方法时,需要加上基类的类名前缀,且需要带上 self 参数变量。区别在于类中调用普通函数时并不需要带上 self 参数
- 3、Python 总是首先查找对应类型的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找。(先在本类中查找调用的方法,找不到才去基类中找)。
如果在继承元组中列了一个以上的类,那么它就被称作"多重继承" 。
语法:
派生类的声明,与他们的父类类似,继承的基类列表跟在类名之后,如下所示:
class SubClassName (ParentClass1[, ParentClass2, ...]):
...
实例
class Parent: # 定义父类
parentAttr = 100
def __init__(self):
print("我是父类构造函数")
def parentMethod(self):
print("调用父类方法")
def setAttr(self, arrt):
Parent.parentAttr = arrt
def getAttr(self):
print("父类属性:", Parent.parentAttr)
class Child(Parent): # 定义子类
def __init__(self):
print("调用子类构造方法")
def childMethod(self):
print("调用子类方child method")
c = Child()
c.childMethod()
c.parentMethod()
c.setAttr(200)
c.getAttr()
以上代码执行结果如下:
调用子类构造方法
调用子类方法
调用父类方法
父类属性 : 200
你可以继承多个类
class A: # 定义类 A
.....
class B: # 定义类 B
.....
class C(A, B): # 继承类 A 和 B
.....
你可以使用issubclass()或者isinstance()方法来检测。
- issubclass() - 布尔函数判断一个类是另一个类的子类或者子孙类,语法:issubclass(sub,sup)
- isinstance(obj, Class) 布尔函数如果obj是Class类的实例对象或者是一个Class子类的实例对象则返回true。
方法重写
如果你的父类方法的功能不能满足你的需求,你可以在子类重写你父类的方法:
实例:
实例
class Animal: # 定义父类
def run(self):
print("Animal is running...") # 调用父类方法
class Cat(Animal): # 定义子类
def run(self):
print("Cat is running....") # 调用子类方法
class Dog(Animal): # 定义子类
def run(self):
print("Dog is running.....") # 调用子类方法
c = Dog() # 子类实例
c.run()
执行以上代码输出结果如下:
Dog is running.....
基础重载方法
下表列出了一些通用的功能,你可以在自己的类重写:
序号 | 方法, 描述 & 简单的调用 |
---|---|
1 | ****init** ( self [,args...] )** 构造函数 简单的调用方法: obj = className(args) |
2 | ****del( self ) 析构方法, 删除一个对象 简单的调用方法 : del obj |
3 | ****repr( self ) 转化为供解释器读取的形式 简单的调用方法 : repr(obj) |
4 | ****str( self ) 用于将值转化为适于人阅读的形式 简单的调用方法 : str(obj) |
5 | ****cmp** ( self, x )** 对象比较 简单的调用方法 : cmp(obj, x) |
运算符重载
Python同样支持运算符重载,实例如下:
实例
#!/usr/bin/python
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)
v1 = Vector(2, 10)
v2 = Vector(5, -2)
print(v1 + v2)
以上代码执行结果如下所示:
Vector(7,8)
类属性与方法
类的私有属性
__private_attrs:两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问。在类内部的方法中使用时 self.__private_attrs。
类的方法
在类的内部,使用 def 关键字可以为类定义一个方法,与一般函数定义不同,类方法必须包含参数 self,且为第一个参数
类的私有方法
__private_method:两个下划线开头,声明该方法为私有方法,不能在类的外部调用。在类的内部调用 self.__private_methods
实例
# !/usr/bin/python # -*- coding: UTF-8 -*-
class JustCounter:
__secretCount = 0
# 私有变量
publicCount = 0
# 公开变量
def count(self):
self.__secretCount += 1
self.publicCount += 1
print(self.__secretCount)
counter = JustCounter()
counter.count()
counter.count()
print(counter.publicCount)
print(counter.__secretCount)
# 报错,实例不能访问私有变量
Python 通过改变名称来包含类名:
1
2
2
Traceback (most recent call last):
File "test.py", line 17, in <module>
print counter.__secretCount # 报错,实例不能访问私有变量
AttributeError: JustCounter instance has no attribute '__secretCount'
Python不允许实例化的类访问私有数据,但你可以使用 object._className__attrName( 对象名.类名_私有属性名** )访问属性,参考以下实例:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
class Runoob:
__site = "www.runoob.com"
runoob = Runoob()
print runoob._Runoob__site
执行以上代码,执行结果如下:
www.runoob.com
单下划线、双下划线、头尾双下划线说明:
- ****foo****: 定义的是特殊方法,一般是系统定义名字 ,类似 ****init() 之类的。
- _foo: 以单下划线开头的表示的是 protected 类型的变量,即保护类型只能允许其本身与子类进行访问,不能用于 **from module import ***
- __foo: 双下划线的表示的是私有类型(private)的变量, 只能是允许这个类本身进行访问了。