Python常用模块之copy、os

Copy

python中的赋值方式:


mylist = [1, 2, 3, 4, 5]
mylist1 = mylist
mylist2 = mylist[:]
#mylist2 = copy.copy(mylist)

这两种赋值方式的效果相同,mylist1和mylist2中的值都是 [1, 2, 3, 4, 5]。但明显,他们的本质不同:

mylist1是在mylist这个变量名的旁边重新贴了一个变量名。也就是说mylist1是变量名mylist所指向的内存新添加的一个标签!mylist1和mylist是不同的地址但指向的是同一个内存!而mylist2是将mylist中存放的所有数据放入mylist2这个变量名所指向的内存中。产生了新的数据!

我们用list对象的pop方法来检验一下mylist1和mylist2之间的区别:


mylist.pop()
print(mylist1)
[1, 2, 3, 4]
print(mylist2)
[1, 2, 3, 4, 5]

mylist1.pop()
print(mylist)
[1, 2, 3]

可以看出pop掉mylist中的数据,mylist1中的数据也发生了相应改变,pop掉mylist1中的数据,mylist也随之变化。而mylist2中的数据并没有发生任何变化,这也验证了我们上面的分析。

对于列表这种数据结构,mylist2 = mylist[:],等价于mylist2 = copy.copy(mylist),也即切片和copy.copy都是浅拷贝,只copy第一层数据,深层的数据仍然与原来的数据共享内存。再举一例。


import copy
numlist = [1, 2, 3, [4, 5, 6]]
#用三种方式进行拷贝
numlist1 = numlist
numlist2 = numlist[:]
numlist3 = copy.deepcopy(numlist)

此时三个列表的值均为 [1, 2, 3, [4, 5, 6]],我们删去numlist的第一个元素,观察输出结果:


numlist.pop(0)
>>>numlist
[2, 3, [4, 5, 6]]
>>>numlist1
[2, 3, [4, 5, 6]]
>>>numlist2
[1, 2, 3, [4, 5, 6]]
>>>numlist3
[1, 2, 3, [4, 5, 6]]

此时删去第一层(浅层),浅拷贝和深拷贝得到的列表内容都没有发生改变。


numlist[2].pop(0)
>>>numlist
[2, 3, [5, 6]]
>>>numlist1
[2, 3, [5, 6]]
>>>numlist2
[1, 2, 3, [5, 6]]
>>>numlist3
[1, 2, 3, [4, 5, 6]]

我们删除第二层(深层),深拷贝得到的列表不发生改变,而浅拷贝得到的列表中的子列表发生了改变!!!这说明浅拷贝得到的列表仅仅复制了表面一层,深层的数据仍和元数据共享一块内存。

联想到静态tuple不可变的原因:不可改变元组tpl中每个元素的地址指向的内容tpl[i],但如果某个元素是一个列表:比tpl[s] = [1, 2, 3],此时tpl[s]这个地址指向的是一个列表的地址,这个列表的地址是不可改变的,但这个地址指向的内容仍然是可以改变的。

为了保证我们对原始数据的修改不影响其他赋值变量的值,要记住赋值的时候不能简简单单地一个等号就以为创建了一个新的变量并赋值了。下面再举一例:


myarr = np.arange(9).reshape(3, 3)
arr1 = arr1
arr2 = arr1[:, :]
arr3 = copy.copy(myarr)
arr4 = copy.deepcopy(myarr)

np.random.shuffle(arr1) #对myarr进行行之间的打乱顺序

结果发现arr1,arr2中的数据随myarr都改变了,而arr3, arr4中的数据还是按照从左到右,从上到下的升序排列。不是说[ : ]的方法是把值赋给一个新的地址吗?怎么arr2也随之变化了呢?

这是因为‘切片=浅拷贝’这一规则是对于list这种数据结构来说的,而数据是一种更为复杂的数据结构,如果想做到修改myarr不改变 arr1的效果,我们就不能再用切片了,必须用copy方法。

其中copy()和deepcopy()都是开辟新内存,修改深层内容也不会改变浅拷贝或者深拷贝来的数组。而切片并没有开辟新的内存。对于数组来说,深拷贝和浅拷贝并没有什么区别,以后拷贝数组就用Obj.copy()方法好了


mylist = [1, 2, 3, [4, 5, 6]]
myarr = np.arange(9).reshape(3, 3)

list1 = copy.deepcopy(mylist)
arr1 = myarr.copy()

os

获取路径:listdir vs walk

我们如果想要打开数据集文件夹中的n个txt文档,并获取其中的摘要要怎么做呢?前面学习过python中对于文件的操作。比如:


with open('D:/#学习资料/数据挖掘实践项目/aan/papers_text/H92-1075.txt', 'rb') as data:

但是这种方法是我们给一个文件路径,它就给打开一个txt。那我们想要依次打开n个txt,就有了一个新思路:依次给它n个txt中第k个的路径,这样他就可以依次打开n个txt中第k篇了。

而该文件夹下的所有文件的路径我们可以通过os模块中的walk函数轻松获取。


import os
import os.path

filePaths = []
for root, dirs, files in os.walk(
    r"D:\MyProjects\Python\DataMining"
):
    for filename in files:
        filePaths.append(os.path.join(root, filename))

我们还可以利用os.listdir方法获取给定文件夹下的所有一级文件夹,文件的名字,比如:


filenames = os.listdir(r'C:\Users\lenovof\Desktop\Matlab作业')

Out[11]: 
['01 2015302045',
 '02 2015302045',
 '03 2015302045',
 '04 2015302045',
 '05 2015302045',
 '06 2015302045',
 '07 2015302045',
 '07 2015302045 temp',
 'GUI实例程序',
 'Simulink实例和上机要求',
 '《MATLAB的S-Function编写指导》.pdf',
 '第八周上机实验内容.pdf',
 '贾秋玲《基于MATLAB 7.X SIMULINK_STATEFLOW系统仿真、分析及设计》.pdf',
 '飞控作业:16个简化公式推导.pdf']

获取到的字符串,不仅有文件名,还有文件夹名,这个时候如果直接传入这些字符串,用withopen打开肯定不行。所以在当前目录下有子目录的情况下,我们倾向于使用os.walk函数。如果没有子目录的话,用listdir就更简洁啦。

与返回一个列表的os.listdir(顾名思义list)不同,os.walk函数返回的是一个生成器,每次访问会返回当前的值,并不断更新。我们来看一下他都返回了什么。


filenames2 =os.walk(r'C:\Users\lenovof\Desktop\Matlab作业')

next(filenames2)
Out[23]: 
('C:\\Users\\lenovof\\Desktop\\Matlab作业',
 ['01 2015302045',
  '02 2015302045',
  '03 2015302045',
  '04 2015302045',
  '05 2015302045',
  '06 2015302045',
  '07 2015302045',
  '07 2015302045temp',
  'GUI实例程序',
  'Simulink实例和上机要求'],
 ['《MATLAB的S-Function编写指导》.pdf',
  '第八周上机实验内容.pdf',
  '贾秋玲《基于MATLAB 7.X SIMULINK_STATEFLOW系统仿真、分析及设计》.pdf',
  '飞控作业:16个简化公式推导.pdf'])

next(filenames2)
Out[24]: 
('C:\\Users\\lenovof\\Desktop\\Matlab作业\\01 2015302045',
 [],
 ['Matlab Tesk1.docx', '飞控第二章公式推导.docx'])

next(filenames2)
Out[25]: 
('C:\\Users\\lenovof\\Desktop\\Matlab作业\\02 2015302045',
 [],
 ['Matlab Task 2.docx', '飞控作业两道题.docx'])

next(filenames2)
Out[26]: 
('C:\\Users\\lenovof\\Desktop\\Matlab作业\\03 2015302045',
 [],
 ['Matlab Task3.docx', 'Matlab Task3.md'])

next(filenames2)
Out[27]: 
('C:\\Users\\lenovof\\Desktop\\Matlab作业\\04 2015302045',
 [],
 ['callcheck.m',
  'calledit.m',
  'Matlab Task 4.docx',
  'mygui1.fig',
  'mygui1.m',
  'mygui11.fig',
  'mygui11.m',
  'mygui2.fig',
  'mygui2.m',
  'mygui2_2.fig',
  'mygui2_2.m',
  'mygui3.fig',
  'mygui3.m',
  'mygui4.fig',
  'mygui4.m',
  'mygui4_1.fig',
  'mygui4_1.m',
  'mygui5.fig',
  'mygui5.m',
  'mygui6.fig',
  'mygui6.m',
  'mygui7.fig',
  'mygui7.m',
  'mygui8.fig',
  'mygui8.m',
  'mygui8_2.fig',
  'mygui8_2.m',
  'mygui9.fig',
  'mygui9.m',
  'mygui_10.fig',
  'mygui_10.m'])

next(filenames2)
Out[28]: 
('C:\\Users\\lenovof\\Desktop\\Matlab作业\\05 2015302045',
 [],
 ['05 2015302045 刘森.docx',
  'SimulinkTask8_1.slx',
  'SimulinkTask8_2.slx',
  'SimulinkTask8_3.slx',
  'SimulinkTask8_4.slx',
  'SimulinkTask8_5.slx',
  'SimulinkTask8_6.slx',
  'SimulinkTask8_7.slx'])

os.walk返回三个值root, dirs, files。root是当前访问的文件夹名,如果当前文件夹root下有子文件夹,则第二个参数dir中会显示子文件夹名,files中就是当前文件夹下所有的文件名啦。

os.walk的逻辑是,先获取当前文件夹下的所有内容(文件或者文件夹)的名字,再依次获取子文件夹中的内容的名字,此时的root就是当前子文件夹的路径。

os.path.join(path, name)函数可以将父目录和文件名,或者子目录连接起来形成一个完整的路径。

此时的filePaths中存放的就是"D:\MyProjects\Python\DataMining"这个文件夹下的所有文件的名字。我们可以利用它配合with open来打开每个文件。


for files in filepaths:
    if os.path.splitext(files)[1] == '.py':
    with open(files, 'rb') as data:

因为这个文件夹中既有python的脚本文件.py格式,也有我们想要打开的.txt格式,所以我们必须将两者加以区分,os.path.splitext(files)[1]就是一个获取文件扩展名的好方法,它将返回一个二维元组,元组的第二个元素为文件的拓展名。


mypath = 'D:\MyProjects\Python\DataMining\Bayes Classifier\Classifier.py'
>>>os.path.splitext(mypath)

Out[67]: ('D:\\MyProjects\\Python\\DataMining\\Bayes Classifier\\Classifier', '.py')

既然可以分离路径和扩展名,当然也可以分离路径和文件名


mypath = 'D:\MyProjects\Python\DataMining\Bayes Classifier\Classifier.py'
>>>os.path.split(mypath)

Out[68]: ('D:\\MyProjects\\Python\\DataMining\\Bayes Classifier', 'Classifier.py')  #去掉了文件名前的//

我们来用os.path.split和os.path.splitext来举个例子,我们需要得到所有simulink仿真文件的文件名,也即是找出所有扩展名为’.slx‘的文件的文件名:


for i in range(len(filePaths)):
    if os.path.splitext(filePaths[i])[-1] == '.slx':
        print(os.path.split(filePaths[i])[-1])

SimulinkTask8_1.slx
SimulinkTask8_2.slx
SimulinkTask8_3.slx
SimulinkTask8_4.slx
SimulinkTask8_5.slx
SimulinkTask8_6.slx
SimulinkTask8_7.slx
InversedPendulum_simu.slx
InversedPendulum_simu2.slx
mfunc.slx
mywork3.slx
excise.slx
excise1.slx
level1.slx
level1_2.slx
mynargin.slx

还可以根据文件名找到它所在的目录,此处要用到os.path.abspath(filename)函数:


os.path.abspath('Classifier.py')
Out[72]: 'D:\\MyProjects\\Python\\TutorialExamples\\Classifier.py'

很好奇,os.path.abspath(filename)函数查找路径有区间吗?是全局查找吗?当然不是,它查找的范围就是我们最开始在os.walk(path)函数中传入的路径。

打开文件:with open

既然提到了withopen,那就顺带说一下文件打开的模式:


for files in filepaths:
    if os.path.splitext(files)[1] == '.py':
    with open(files, 'rb') as data:

其中open函数中的'rb'指的是以二进制格式只读取文件。下面的表格写出了各种模式的特点,但记这个表格太复杂,我们说几种最常用的功能就好。

只从头到尾读文件:'r'

从开头写入内容,覆盖原有内容:'r+'

从末尾追加内容:'a'

先删除原来的内容,再从开头写入内容:'w'

创建一个新文件,并打开供写入内容:'x'

模式 r r+ w w+ a a+
+ + + +
+ + + + +
删除原数据 + +
覆盖 +
指针在开始 + + + +
指针在结尾 + +

#读取io test.txt文件中的内容
with open(r'D:\MyProjects\Python\TutorialExamples\io test.txt', 'r') as data:
    lines = []
    for line in data.readlines():
        lines.append(line)
        
lines
Out[82]: 
['ABC;\n',
 'DEFG;\n',
 'HIJK;\n',
 'LMN;\n',
 'OPQR;\n',
 'ST;\n',
 'UVW;\n',
 'XYZ.']

#在末尾追加内容
with open(r'D:\MyProjects\Python\TutorialExamples\io test.txt', 'a+') as data:
    data.write('\nabcdefghigklmnopqrstuvwxyz')

with open(r'D:\MyProjects\Python\TutorialExamples\io test.txt', 'r') as data:
    lines = []
    for line in data.readlines():
        lines.append(line)
        
lines
Out[85]: 
['ABC;\n',
 'DEFG;\n',
 'HIJK;\n',
 'LMN;\n',
 'OPQR;\n',
 'ST;\n',
 'UVW;\n',
 'XYZ.\n',
 'abcdefghigklmnopqrstuvwxyz']


#删除原内容再写入
with open(r'D:\MyProjects\Python\TutorialExamples\io test.txt', 'w+') as data:
    data.write('abcdefghigklmnopqrstuvwxyz')

with open(r'D:\MyProjects\Python\TutorialExamples\io test.txt', 'r') as data:
    lines = []
    for line in data.readlines():
        lines.append(line)
        
lines
Out[91]: ['abcdefghigklmnopqrstuvwxyz']


#在开头写入内容,冲掉与原有的内容
with open(r'D:\MyProjects\Python\TutorialExamples\io test.txt', 'r+') as data:
    data.write('9876543210\n')

with open(r'D:\MyProjects\Python\TutorialExamples\io test.txt', 'r') as data:
    lines = []
    for line in data.readlines():
        lines.append(line)

lines
Out[105]: 
['9876543210\n', 
 'mnopqrstuvwxyz']

r只能读,r+可读可写,写的时候是从头开始写,并且把写入数据位置上原来的数据覆盖掉,保留未覆盖的内容。w只能写,w+可写可读,w和w+是将原有数据全部删除在开始写入内容。a,a+是在末尾追加内容。

要注意的是只有在文件关闭之后,写入内容的操作才会生效,这时候我们再次读取文件,才能获取到修改之后的内容。

我们help(open)查看一下手册:

========= =============================================================== Character Meaning --------- --------------------------------------------------------------- 'r' open for reading (default) 'w' open for writing, truncating the file first 'x' create a new file and open it for writing 'a' open for writing, appending to the end of the file if it exists 'b' binary mode 't' text mode (default) '+' open a disk file for updating (reading and writing) 'U' universal newline mode (deprecated) ========= ===============================================================

你可能感兴趣的:(python)