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