python文件打包成exe可执行文件:
以game2048.py脚本为例,实现打包生成可执行文件。
非计算机软件专业的我从大学开始学习计算机语言后,只知道如何写代码,运行调试,功能实现,总感觉差丢丢儿。后来因为兴趣和工作需要,每每写好一个算法或功能,我只能把源代码拿出去展示?莫名感觉没有档次,总感觉缺一丢丢。写了训练人工智能识别算法智能在cmd或IDE上面弄。换台电脑做算法就心累,环境布置,库安装要削减发量的有木有。所以,自己做不限制环境的工具非常有必要,而且发给朋友同事做数据处理超级适用。本文介绍将python文件打包成exe可执行文件的方法。
关键库:
提示:pyinstaller安装,使用实践。
1、 安装pyinstaller
anaconda大环境下安装运行:conda install pyinstaller
没有conda就在命令行中运行:pip install pyinstaller
2、 打包python文件实例
将编写的2048游戏程序脚本保存在game2048.py中;
把它打包成exe需要在同一文件夹中打开命令行并输入:
pyinstaller -F ./game2048.py
回车后运行,如果一切顺利,文件夹中会多出一个后缀为spec的文件,以及两个名为dist和build的文件夹。
打开dist,可以找到game2048.exe,这就是生成的可执行文件。
3、pyinstaller的其他的选项,参数:
-i: 后接图标文件名,表示用自定义图标生成exe程序
-w: 生成的exe程序不带窗口执行
如果想只运行tkinter 页面,去掉dos窗口需要在打包的时候 加上 -w 参数
eg. pyinstaller -F calc.py -w
注意:
- pyinstaller需安装在脚本所依赖环境env里,或者打包时保证你的脚本运行和所依赖的env一致。比如,用命令行打包有默认python环境,而调试代码用的是另一个环境。
- 生成的exe文件可复制到电脑上的任何地方单独使用。
实践:
游戏脚本game2048.py
注意:游戏界面引入了curses库
,Windows下使用该库的注意事项和安装。
# !/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/8/6 15:15
# @Author : Haiyan T
# @Email : 893190391@qq.com
# @File : game2048.py
# Begin to show your code!
import curses # 绘制在下的用户界面和漂亮的图形
from random import randrange, choice
from collections import defaultdict
letter_codes = [ord(ch) for ch in 'WASDRQwasdrq'] # 上左下右,ord函数是把字符转换成对应的数字
actions = ['Up', 'Left', 'Down', 'Right', 'Restart', 'Exit'] # 上,左,下,右,重启,退出
actions_dict = dict(zip(letter_codes, actions * 2)) # 把字母与动作对应起来。 zip是把元组中的值对应起来。
# print(actions_dict)
def get_user_action(keyboard):
char = "N" # char的初始值为N
while char not in actions_dict:
char = keyboard.getch()
return actions_dict[char] # 阻塞+循环,直到获得用户有效输入才返回对应行为
# 行列转置
def transpose(field):
return [list(row) for row in zip(*field)] # zip函数里边加*号,是把行变列,列变行。
# 列表前后颠倒
def invert(field):
return [row[::-1] for row in field]
# 创建棋盘
class GameField(object):
def __init__(self, height=4, width=4, win=2048):
self.height = height # 高
self.width = width # 宽
self.win_value = win # 过关分数
self.score = 0 # 当前分数
self.highscore = 0 # 最高分
self.reset() # 重置棋盘
def reset(self): # 定义一个reset函数
if self.score > self.highscore: # 如果当前分数大于最高分,那么把当前分数赋值给最高分
self.highscore = self.score
self.score = 0 # 当前分数恢复到0分
self.field = [[0 for i in range(self.width)] for j in range(self.height)] # 横纵坐标恢复到(0,0)
self.spawn() # 调用spawn这个函数
self.spawn()
def move(self, direction): # 定义move函数
def move_row_left(row): # 向左移
def tighten(row): # squeese non-zero elements together 把零散的非零单元挤到一块
new_row = [i for i in row if i != 0] # 如果i不等于零,把他们赋值到new_row这个元组中
new_row += [0 for i in range(len(row) - len(new_row))] # 其余位置用0补充
return new_row # 返回这个元组
def merge(row): # 定义merge函数,用来合并单元
pair = False # pair初始值为假
new_row = [] # new_row初始值为空
for i in range(len(row)): # 让i在格子里循环
if pair: # 如果pair为真
new_row.append(2 * row[i]) # 那么把把row【i】的值乘以2,追加到new_row后边
self.score += 2 * row[i] # 并且得分为row【i】的值乘以2
pair = False # pair重新赋值为假
else: # 如果pair为真
if i + 1 < len(row) and row[i] == row[i + 1]: # 如果i+1还没到边界,并且此时的row【i】=row【i+1】
pair = True # 那么pair为真
new_row.append(0) # new_row后追加零
else:
new_row.append(row[i]) # 否则追加row【i】
assert len(new_row) == len(row) # 提醒两者长度一致
return new_row
return tighten(merge(tighten(row))) # 反复合并,知道不能合并为止
moves = {}
moves['Left'] = lambda field: \
[move_row_left(row) for row in field] # 做移动
moves['Right'] = lambda field: \
invert(moves['Left'](invert(field))) # invert是逆转
moves['Up'] = lambda field: \
transpose(moves['Left'](transpose(field))) # transpose是转置
moves['Down'] = lambda field: \
transpose(moves['Right'](transpose(field)))
if direction in moves:
if self.move_is_possible(direction): # 如果移动方向在四个方向上,
self.field = moves[direction](self.field) # 那么调用moves函数
self.spawn() # 产生随机数
return True
else:
return False
def is_win(self):
return any(any(i >= self.win_value for i in row) for row in self.field)
def is_gameover(self):
return not any(self.move_is_possible(move) for move in actions)
def draw(self, screen):
help_string1 = '(W)Up (S)Down (A)Left (D)Right'
help_string2 = ' (R)Restart (Q)Exit'
gameover_string = ' GAME OVER'
win_string = ' YOU WIN!'
def cast(string):
screen.addstr(string + '\n')
def draw_hor_separator():
line = '+' + ('+------' * self.width + '+')[1:]
separator = defaultdict(lambda: line)
if not hasattr(draw_hor_separator, "counter"):
draw_hor_separator.counter = 0
cast(separator[draw_hor_separator.counter])
draw_hor_separator.counter += 1
def draw_row(row):
cast(''.join('|{: ^5} '.format(num) if num > 0 else '| ' for num in row) + '|')
screen.clear()
cast('SCORE: ' + str(self.score))
if 0 != self.highscore:
cast('HGHSCORE: ' + str(self.highscore))
for row in self.field:
draw_hor_separator()
draw_row(row)
draw_hor_separator()
if self.is_win():
cast(win_string)
else:
if self.is_gameover():
cast(gameover_string)
else:
cast(help_string1)
cast(help_string2)
def spawn(self):
new_element = 4 if randrange(100) > 89 else 2
(i, j) = choice([(i, j) for i in range(self.width) for j in range(self.height) if self.field[i][j] == 0])
self.field[i][j] = new_element
def move_is_possible(self, direction):
def row_is_left_movable(row):
def change(i): # true if there'll be change in i-th tile
if row[i] == 0 and row[i + 1] != 0: # Move
return True
if row[i] != 0 and row[i + 1] == row[i]: # Merge
return True
return False
return any(change(i) for i in range(len(row) - 1))
check = {}
check['Left'] = lambda field: \
any(row_is_left_movable(row) for row in field)
check['Right'] = lambda field: \
check['Left'](invert(field))
check['Up'] = lambda field: \
check['Left'](transpose(field))
check['Down'] = lambda field: \
check['Right'](transpose(field))
if direction in check:
return check[direction](self.field)
else:
return False
def main(stdscr):
def init():
# 重置游戏棋盘
game_field.reset()
return 'Game'
def not_game(state):
# 画出 GameOver 或者 Win 的界面
game_field.draw(stdscr)
# 读取用户输入得到action,判断是重启游戏还是结束游戏
action = get_user_action(stdscr)
responses = defaultdict(lambda: state) # 默认是当前状态,没有行为就会一直在当前界面循环
responses['Restart'], responses['Exit'] = 'Init', 'Exit' # 对应不同的行为转换到不同的状态
return responses[action]
def game():
# 画出当前棋盘状态
game_field.draw(stdscr)
# 读取用户输入得到action
action = get_user_action(stdscr)
if action == 'Restart':
return 'Init'
if action == 'Exit':
return 'Exit'
if game_field.move(action): # move successful
if game_field.is_win():
return 'Win'
if game_field.is_gameover():
return 'Gameover'
return 'Game'
state_actions = {
'Init': init,
'Win': lambda: not_game('Win'),
'Gameover': lambda: not_game('Gameover'),
'Game': game
}
curses.use_default_colors()
game_field = GameField(win=2048)
state = 'Init'
# 状态机开始循环
while state != 'Exit':
state = state_actions[state]()
curses.wrapper(main)
anaconda prompt下运行打包:
(DeepLearningSlideCaptcha) C:\Users\Administrator>pyinstaller -F E:\structure_learning\0304t\2048\game2048.py
- (DeepLearningSlideCaptcha) 环境,可使用
activate
切换。 - pyinstaller -F E:\structure_learning\0304t\2048\game2048.py 命令。
- 结果
......
7280 INFO: checking EXE
7281 INFO: Building EXE because EXE-00.toc is non existent
7281 INFO: Building EXE from EXE-00.toc
7281 INFO: Appending archive to EXE C:\Users\Administrator\dist\game2048.exe
7286 INFO: Building EXE from EXE-00.toc completed successfully.
测试:
- 直接打包
生成的exe程序如下:
双击运行
2. 带图标打包
pyinstaller -F -i 图标地址 脚本地址
回车,打包完成
pyinstaller -F -i E:\TanhaiyanLearn\structure_learning\0304t\2048\2048.ico E:\TanhaiyanLearn\structure_learning\0304t\2048\game2048.pyE:\TanhaiyanLearn\structure_learning\0304t\2048\2048.ico E:\TanhaiyanLearn\structure_learning\0304t\2048\game2048.py