内容简介:python错误:module ‘shutil’ has no attribute ‘copy’
字符:5706
平移阅读时长:7分钟
今天在写一个小脚本来把自己写的一个html批量复制并在保持后缀不变的情况下随机命名。
先打算实现文件的拷贝。
Google了一下,发现了一个 shutil
的库(这个库的名字纠结了发音老半天,后面才理解是sh和util的结合,也就是shell-util)
这个库提供了与 shell 命令操作文件等同的所有方法: copy
, copytree
, rmtree
, move
…
代码实现
有了这个库,代码很容易就实现了:
import shutil src = 'sa-jumper.html' randomStr = 'ad239sdfasdl2asdf9adsfi23dfladf' strLen = len(randomStr) shutil.copy(src, 'tmp/aa.html')
关于随机的部分,就只是提前做了准备。先用的 shutil.copy
来测试文件拷贝功能。
然后我先用的 python3.6 执行了这个文件,结果报错:
Traceback (most recent call last):
File "copy.py", line 2, in <module>
import shutil
File "/usr/local/Cellar/python3/3.6.0_1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/shutil.py", line 13, in <module>
import tarfile
File "/usr/local/Cellar/python3/3.6.0_1/Frameworks/Python.framework/Versions/3.6/lib/python3.6/tarfile.py", line 49, in <module>
import copy
File "/Library/WebServer/Documents/xxx/copy.py", line 13, in <module>
shutil.copy(src, 'tmp/aa.html')
AttributeError: module 'shutil' has no attribute 'copy'
后面改用 python2.7 执行,顺利通过。
debug
打开 Python 的解释器提示符,执行上述代码,也是成功的。
这下我就没辙了,感觉个人的Python经验只能到此为止,启用Google大法。
在StackOverflow的一篇文章上,发现有人遇到类似的问题,链接如下:
https://stackoverflow.com/questions/22131139/attributeerror-module-object-has-no-attribute-x
在上述链接里面,最终的错误是因为提问的人,python脚本的名称是 org.py
跟系统的一个文件重名,导致了一个错误的引用。
所以很自然的,我就联想到了我的文件,名字是 copy.py
,而看上面的错误信息里面,是用一个 import copy
,估计也是同样错误的把我的脚本当作官方库给错误引用了。
修复
把文件名改成了 copy-file.py
,再用 python3.6
执行,一切顺利。
分析
从这个错误里面想到几个问题:
- 为啥跟官方库里面的文件同名会导致引用错误
- 为啥python2.7没报错
- 如何避免这样的问题
关于引用错误
重新翻了一下之前看的《Python简明教程》
关于模块的索引
如果它不是一个已编译好的模块,即用 Python 编写的模块,那么 Python 解释器将从它的 sys.path 变量所提供的目录中进行搜索。如果找到了对应模块,则该模块中的语句将在开始运行,并能够为你所使用。在这里需要注意的是,初始化工作只需在我们第一次导入模块时完成。
关于索引的顺序
sys.path 内包含了导入模块的字典名称列表。你能观察到 sys.path 的第一段字符串是空的——这一空字符串代表当前目录也是 sys.path 的一部分,它与 PYTHONPATH 环境变量等同。这意味着你可以直接导入位于当前目录的模块。否则,你必须将你的模块放置在 sys.path 内所列出的目录中。
从这里可以看到,执行的脚本所在的目录也是在python搜索模块的范围内的,而且是第一位,也就是会优先搜索的,这也就是为啥同名会导致引用错误的原因了。
为啥2.7没错
按照上面StackOverflow那边文章的分析过程,我分别看了python3.6和python2.7版本里面 shutil.py
和 tarfile.py
源码,发现 shutil.py
都引用了 tarfile.py
,而 tarfile.py
也都 import copy
- https://hg.python.org/cpython/file/2.7/Lib/shutil.py
- https://github.com/python/cpython/blob/3.6/Lib/shutil.py
- https://hg.python.org/cpython/file/2.7/Lib/tarfile.py
- https://github.com/python/cpython/blob/3.6/Lib/tarfile.py
那就不可能是2.7里面没有引用copy了。
然后我又仔细的看了一下各自的实现,终于发现了问题,在2.7里面,不是在初始化的时候就import了tarfile,而是在一个 _make_tarball
的函数里面。
我在这个函数的 import tarfile
前面加了一个 print('import tarfile')
,再去执行刚刚的 copy.py
(这块为了测试方便,把 copy-file.py
复制了一份重命名为 copy.py
)。
发现没有print任何内容。
而在3.6里面,是在文件头部,一开始就import了tarfile。这也就解释了为啥2.7没问题,3.6有问题了。同时也说明: python的import是按需加载的 。写在函数里面的import,只有在函数被调用的时候才会真实的去import
为了反证这个推断是否有效,我找到了2.7里面shutil的一个会触发 import tarfile
的函数 make_archive
使用了官方提供的example:
import shutil
import os
archive_name = os.path.expanduser(os.path.join('~', 'myarchive'))
root_dir = os.path.expanduser(os.path.join('~', '.ssh'))
shutil.make_archive(archive_name, 'gztar', root_dir)
执行 python copy.py
(我的环境里面,默认是2.7)
果然报错了:
import tarfile
import tarfile
Traceback (most recent call last):
File "copy.py", line 20, in <module>
shutil.make_archive(archive_name, 'gztar', root_dir)
File "/usr/local/Cellar/python/2.7.13/Frameworks/Python.framework/Versions/2.7/lib/python2.7/shutil.py", line 561, in make_archive
filename = func(base_name, base_dir, **kwargs)
File "/usr/local/Cellar/python/2.7.13/Frameworks/Python.framework/Versions/2.7/lib/python2.7/shutil.py", line 376, in _make_tarball
import tarfile # late import so Python build itself doesn't break
File "/usr/local/Cellar/python/2.7.13/Frameworks/Python.framework/Versions/2.7/lib/python2.7/tarfile.py", line 52, in <module>
import copy
File "/Library/WebServer/Documents/xxx/copy.py", line 20, in <module>
shutil.make_archive(archive_name, 'gztar', root_dir)
File "/usr/local/Cellar/python/2.7.13/Frameworks/Python.framework/Versions/2.7/lib/python2.7/shutil.py", line 561, in make_archive
filename = func(base_name, base_dir, **kwargs)
File "/usr/local/Cellar/python/2.7.13/Frameworks/Python.framework/Versions/2.7/lib/python2.7/shutil.py", line 394, in _make_tarball
tar = tarfile.open(archive_name, 'w|%s' % tar_compression[compress])
AttributeError: 'module' object has no attribute 'open'
然后我尝试把文件名修改为 copy-file.py
,再执行,顺利通过。
看来2.7和3.6都是一样的索引模块的方式,只是因为 shutil
模块里面引用 tarfile
的方式不一样,才造成一开始的执行结果不一样。
如何避免这样的问题
我还是一个python新手,不知道python在命名的时候有没有提供一些合理的规范,比如不能使用的单词。
然后我又一次看了《简明Python教程》,里面关于标识符命名这块:
量是标识符的一个例子。标识符(Identifiers) 是为 某些东西 提供的给定名称。在你命名标识符时,你需要遵守以下规则:
)。
)、数字(0~9)组成。• 标识符名称区分大小写。例如,myname 和 myName 并不等同。要注意到前者是小写字母 n 而后者是大写字母 N。
• 有效 的标识符名称可以是 i 或 name_2_3 ,无效 的标识符名称可能是 2things,this is spaced out,my-name 和 >a1b2_c3。
我想,如果我以非这个规范的方式定义文件名是不是就可以了。
然而用类似 import copy-file
的方式引用自己的模块,会报错, SyntaxError
。
也有看到有相关的解决方式:
-
python2.7
execfile('foo-bar.py') -
python3.x
exec(open(fn).read())
不过这些方法感觉都不是很自然,还是放弃这样的尝试了。
只能使用另一种方式了,就是统一文件名的前缀。如果你的项目是 foo
,那么可以给所有项目的文件定义以这个为前缀的名称: foo__copy.py
, foo_shutil.py
等等。
这样只要 foo
是一个不会和常见的库的前缀重名的字符串,就能保证项目里面不会再发生这样的名称冲突了。
参考资料
- https://stackoverflow.com/questions/123198/how-do-i-copy-a-file-in-python
- https://stackoverflow.com/questions/22131139/attributeerror-module-object-has-no-attribute-x
- https://docs.python.org/2/library/shutil.html
- https://stackoverflow.com/questions/8350853/how-to-import-module-when-module-name-has-a-dash-or-hyphen-in-it
- https://stackoverflow.com/questions/6031584/importing-from-builtin-library-when-module-with-same-name-exists
- https://bop.molun.net/07.basics.html
- https://bop.molun.net/11.modules.html
突然发现,我只是想实现一个文件拷贝,咋就绕了这么远呢:sob:
以上所述就是小编给大家介绍的《python错误:module ‘shutil’ has no attribute ‘copy’》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Golang学习笔记之错误处理error、panic (抛出错误),recover(捕获错误)
- c – 构建PBRT v2错误 – 错误1错误U1077:’if’:返回代码’0x1′
- !错误!在 Android 下这么用 ShowModal 是错误的!
- Google开源ClusterFuzz:使得查找错误并修复错误变得异常简单
- 脚本错误量极致优化-定位压缩且无 SourceMap 文件的脚本错误
- php – 解析错误:语法错误,意外’未设置'(T_UNSET)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Is Parallel Programming Hard, And, If So, What Can You Do About
Paul E. McKenney
The purpose of this book is to help you understand how to program shared-memory parallel machines without risking your sanity.1 By describing the algorithms and designs that have worked well in the pa......一起来看看 《Is Parallel Programming Hard, And, If So, What Can You Do About 》 这本书的介绍吧!