【问题标题】:Compiling a tkinter game编译一个 tkinter 游戏
【发布时间】:2015-02-18 02:05:10
【问题描述】:

我了解如何使用 py2exe、可移植 python 和其他方式编译程序的一般过程,并且总是了解一些可能导致问题的问题,例如 matplotlib 等。但是,我很好奇编译器将如何工作如果游戏使用泡菜。如果已编译,游戏是否仍然能够保存和加载状态,或者它不再能够拥有此选项?

另外,如果有人不介意我对编译程序的实际工作方式感到有点困惑,就像编译器为了使您的程序成为可执行文件所经历的过程一样,对此过程的一般解释会很棒。

【问题讨论】:

  • 你用过其他语言吗?如果我相信正确(警告!我可能错了),Python 在运行或初始化时会编译代码,而其他语言(例如 Java)在代码完成后必须经过整个编译过程?
  • 我并没有打算学习 C++,但最近没有时间,python 是我唯一用过的编程语言。我记得有些类似的东西。我知道python可以调用C写的代码,不知道能不能编译C。
  • 我不太确定... :(

标签: python compilation compiler-errors tkinter pickle


【解决方案1】:

基本上,python 使用语言解析器解释代码行......然后将解析的行编译为字节码。这个字节码是“编译的python”。

让我们编写一些代码:

# file: foo.py 
class Bar(object):
  x = 1
  def __init__(self, y):
    self.y = y

现在我们导入它。

>>> import foo
>>> foo
<module 'foo' from 'foo.py'>
>>> reload(foo)
<module 'foo' from 'foo.pyc'>

您会注意到,我们第一次导入foo 时,它说它是从foo.py 导入的。那是因为 python 必须将代码字节编译成模块对象。但是,这样做会在您的目录中留下一个 .pyc 文件......这是一个编译的 python 文件。 Python 更喜欢使用编译后的代码,作为一种节省时间的方式,而不是再次编译代码......所以当你 reload 模块时,python 会选择要导入的编译后的代码。基本上,当你“安装”python 模块时,你只是将编译后的代码移动到 python 可以导入它的地方(在你的 PYTHONPATH 上)。

>>> import numpy
>>> numpy
<module 'numpy' from '/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/numpy/__init__.pyc'>

site-packages 目录是安装已编译的第 3 方代码的默认位置。实际上,模块只是文件的 python 对象表示。意思是,模块实例是一个编译文件。一旦你将文件“编译”到一个模块中,它就不再关心文件中的内容......python只需要在那之后编译的字节码。

>>> import types
>>> types.ModuleType.mro()
[<type 'module'>, <type 'object'>]
>>> foo.__class__.mro()
[<type 'module'>, <type 'object'>]
>>> i = object()
>>> object
<type 'object'>
>>> i
<object object at 0x1056f60b0>

在这里我们看到(使用typesfooModuleType 的一个实例...所以基本上是一个编译文件。 mro 显示模块继承自 python object,这是 python 中的主要对象。 (是的,它是面向对象的)。

这里iobject 的一个实例,就像fooModuleType 的一个实例一样。 Python 使用编译对象的实例,而不是底层代码......就像(几乎?)所有其他语言一样。因此,当您使用在 foo 模块中构建的类时,您正在使用该类的字节编译实例。您可以通过动态添加方法来动态修改类实例...它不会更改基础文件foo.py...但它确实会更改模块foo的字节编译实例保存在内存中。

>>> zap = foo.Bar(2)
>>> zap.x, zap.y
(1, 2)
>>> foo.Bar 
<class 'foo.Bar'>
>>> foo.Bar.mro()
[<class 'foo.Bar'>, <type 'object'>]
>>>        
>>> def wow(self): 
...   return self.x + self.y
... 
>>> wow(zap)
3
>>> foo.Bar.wow = wow
>>> foo.Bar.wow(zap)
3
>>> zap.wow()
3

同样,文件foo.py 将保持不变......但是,我将wow 添加到类Bar,所以它就像wow 首先在代码中一样可用。因此,使用“已编译的”python 根本不是静态的……它只是意味着您正在使用经过字节编译的代码,以便在您第一次导入它时节省一些时间。请注意,由于模块 foo 是一个实例,您还可以在内存中编辑它(不仅仅是已经存在于其内容中的对象)。

>>> foo.square = lambda x:x**2
>>>            
>>> from foo import square
>>> square(3)
9

在这里,我将squared 添加到foo——不是添加到foo.py,而是添加到内存中foo 的字节编译副本。

那么你能在编译后的代码中腌制和取消腌制对象吗?绝对地。如果您使用过pickle,您可能已经这样做了。

附:如果您正在谈论构建 python 的 C++ 扩展,并将代码编译到共享库......它仍然基本上没有什么不同。

如果您正在寻找有关字节编译的详细信息,请在此处查看我的问题和答案:How is a python function's name reference found inside it's declaration?

【讨论】:

    猜你喜欢
    • 2020-05-17
    • 2010-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-29
    • 2022-01-12
    • 1970-01-01
    • 2020-05-22
    相关资源
    最近更新 更多