运行cx_Freeze可执行文件遇AttributeError:tensorflow_estimator模块无__file__属性
AttributeError: module has no attribute '__file__'问题 问题回顾
你说你尝试编译这段包含Keras、TensorFlow和其他依赖的Python脚本:
from keras import optimizers from keras.models import Sequential from keras.models import model_from_json from keras.layers import Dense, Flatten import time import tkinter import cv2 import PIL.Image, PIL.ImageTk from matplotlib import pyplot as plt import numpy as np import pickle from pathlib import Path import threading input("waiting for input") exit()
用的打包命令是:
cxfreeze test.py --target-dir dist --include-modules=numpy.core._methods,numpy.lib.format,tensorflow.python.tf2,tensorflow.python.tools.component_api_helper
结果运行生成的可执行文件时触发了错误,完整报错日志如下:
davidaffo@davidaffo-TM1701:~/PycharmProjects/EyeTracker$ source ./venv/bin/activate
(venv) davidaffo@davidaffo-TM1701:~/PycharmProjects/EyeTracker$ ./dist/test
Using TensorFlow backend.
Traceback (most recent call last):
File "/home/davidaffo/PycharmProjects/EyeTracker/venv/lib/python3.6/site-packages/cx_Freeze/initscripts/startup.py", line 14, in run
module.run()
File "/home/davidaffo/PycharmProjects/EyeTracker/venv/lib/python3.6/site-packages/cx_Freeze/initscripts/Console.py", line 26, in run
exec(code, m.dict)
File "test.py", line 1, in
from keras import optimizers
File "/home/davidaffo/PycharmProjects/EyeTracker/venv/lib/python3.6/site-packages/keras/init.py", line 3, in
from . import utils
File "/home/davidaffo/PycharmProjects/EyeTracker/venv/lib/python3.6/site-packages/keras/utils/init.py", line 6, in
from . import conv_utils
File "/home/davidaffo/PycharmProjects/EyeTracker/venv/lib/python3.6/site-packages/keras/utils/conv_utils.py", line 9, in
from .. import backend as K
File "/home/davidaffo/PycharmProjects/EyeTracker/venv/lib/python3.6/site-packages/keras/backend/init.py", line 89, in
from .tensorflow_backend import *
File "/home/davidaffo/PycharmProjects/EyeTracker/venv/lib/python3.6/site-packages/keras/backend/tensorflow_backend.py", line 5, in
import tensorflow as tf
File "/home/davidaffo/PycharmProjects/EyeTracker/venv/lib/python3.6/site-packages/tensorflow/init.py", line 29, in
from tensorflow._api.v1 import compat
File "/home/davidaffo/PycharmProjects/EyeTracker/venv/lib/python3.6/site-packages/tensorflow/_api/v1/compat/init.py", line 21, in
from tensorflow._api.v1.compat import v1
File "/home/davidaffo/PycharmProjects/EyeTracker/venv/lib/python3.6/site-packages/tensorflow/_api/v1/compat/v1/init.py", line 626, in
child_package_str=('tensorflow_estimator.python.estimator.api.estimator'))
File "/home/davidaffo/PycharmProjects/EyeTracker/venv/lib/python3.6/site-packages/tensorflow/python/tools/component_api_helper.py", line 85, in package_hook
set_child_as_subpackage()
File "/home/davidaffo/PycharmProjects/EyeTracker/venv/lib/python3.6/site-packages/tensorflow/python/tools/component_api_helper.py", line 69, in set_child_as_subpackage
os.path.join(os.path.dirname(child_pkg.file), ".."))]
AttributeError: module 'tensorflow_estimator.python.estimator.api.estimator' has no attribute 'file'
错误原因
这个问题的根源在于tensorflow_estimator的API模块是动态生成的,它不像普通Python模块那样有实际的文件路径,自然也就没有__file__属性。但cx_Freeze在打包和运行时,会尝试读取这个属性来处理模块路径,于是就触发了这个错误。
解决方案
我整理了几个可行的解决办法,你可以按顺序尝试:
1. 用自定义setup.py精细控制打包配置
命令行打包的灵活性有限,建议创建一个setup.py文件来管理打包过程,这样可以排除掉有问题的模块:
import sys from cx_Freeze import setup, Executable # 你的目标脚本文件名 script_name = "test.py" # 控制台程序可以去掉base参数,GUI程序在Windows下用"Win32GUI" base = None if sys.platform == "win32": base = "Win32GUI" executables = [Executable(script_name, base=base)] setup( name="EyeTracker", version="0.1", description="Your eye tracking application", executables=executables, options={ "build_exe": { # 包含必要的模块 "includes": [ "numpy.core._methods", "numpy.lib.format", "tensorflow.python.tf2", "tensorflow.python.tools.component_api_helper", "keras" ], # 排除那个有问题的动态模块 "excludes": [ "tensorflow_estimator.python.estimator.api.estimator" ], # 明确指定要打包的大型库 "packages": ["tensorflow", "keras"] } } )
然后运行这个命令打包:
python setup.py build
2. 降级到更兼容的TensorFlow版本
你当前用的是Python 3.6,而较新的TensorFlow 2.x版本在动态模块生成上和cx_Freeze的兼容性不太好。可以尝试降级到TensorFlow 1.x的稳定版本,比如1.15.5,搭配对应的Keras版本:
pip install tensorflow==1.15.5 keras==2.3.1
安装完成后,先在虚拟环境里正常运行脚本确认没问题,再重新打包试试。
3. 临时修改TensorFlow的源码(应急方案)
如果上面的方法都不奏效,你可以手动修改TensorFlow的component_api_helper.py文件来规避这个错误:
找到你虚拟环境中该文件的路径:/home/davidaffo/PycharmProjects/EyeTracker/venv/lib/python3.6/site-packages/tensorflow/python/tools/component_api_helper.py
打开文件,找到set_child_as_subpackage函数,修改第69行左右的代码,添加一个判断,检查模块是否有__file__属性:
# 原代码 os.path.join(os.path.dirname(child_pkg.__file__), ".."))] # 修改后的代码 if hasattr(child_pkg, '__file__'): pkg_path = os.path.join(os.path.dirname(child_pkg.__file__), "..") else: pkg_path = "" # 或者直接跳过这部分处理
修改后保存,重新打包即可。
额外小提示
- 打包前一定要确保你的虚拟环境里能正常运行脚本,先排除代码本身的问题。
- cx_Freeze对TensorFlow这类大型库的支持一直有点麻烦,如果折腾半天还是不行,可以试试PyInstaller,社区里针对它的TensorFlow打包解决方案更多。
内容的提问来源于stack exchange,提问作者Davide Daffonchio

