Compiling a tkinter game Compiling a tkinter game tkinter tkinter

Compiling a tkinter game


Basically, python interprets the lines of code with the language parser... and then compiles the parsed lines to byte code. This byte code is "compiled python".

Let's build a bit of code:

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

Now we import it.

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

What you'll notice is that the first time we import foo, it says it was imported from foo.py. That's because python had to byte compile the code into a module object. Doing so, however, leaves a .pyc file in your directory... that's a compiled python file. Python prefers to use compiled code, as a time-saver as opposed to compiling the code again... so when you reload the module, python picks the compiled code to import. Basically, when you are "installing" python modules, you are just moving compiled code into somewhere python can import it (on your PYTHONPATH).

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

The site-packages directory is the default place that compiled 3rd party code gets installed. Indeed a module is just a python object representation of a file. Meaning, a module instance is a compiled file. Once you "compile" the file in to a module, it's no longer going to care what's in the file... python only needs the compiled byte code after that.

>>> 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>

Here we see (using types) that foo is an instance of a ModuleType... so basically a compiled file. mro shows modules inherit from a python object, which is the primary object in python. (Yes, it's object-oriented).

Here i is an instance of an object, just as foo is an instance of a ModuleType. Python works with instances of compiled objects, not the underlying code... just like (almost?) every other language. So, when you work with a class that you built in the foo module, you are working with the byte compiled instance of the class. You can dynamically modify the class instance, by adding methods on-the-fly... and it doesn't change the underlying file foo.py... but it does alter the byte-compiled instance of the module foo that's held in memory.

>>> 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

Again, the file foo.py would be unchanged... however, I added wow to the class Bar, so it's usable as if wow were in the code in the first place. So working with "compiled" python is not static at all... it just means that you are working with code that has been byte compiled to save some time when you are importing it the first time. Note that since the module foo is an instance, you can also edit it in memory (not just objects that already live in it's contents).

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

Here I added squared to foo -- not to foo.py, but to the byte compiled copy of foo that lives in memory.

So can you pickle and unpickle objects in compiled code? Absolutely. You are probably doing that already if you've used pickle.

P.S. If you are talking about building C++ extensions to python, and compiling the code to shared libraries... it's still fundamentally no different.

If you are looking for some nitty-gritty details on byte compiling, check out my question and answer here: How is a python function's name reference found inside it's declaration?.