Hi Sam,

It doesn't honestly sound like your file is too big yet, from a Python
perspective.  I've seen MUCH longer single scripts, and it's tough to
really say without an idea of the purpose or structure of your script what
the best way to split it up is.  In my experience (I come from a C++/C#
background), Python likes to have much more monolithic scripts than I'm
used to.  I've always preferred to make one class per file, and maybe
several files with utility functions.  In Python, however, this will
quickly run you into problems with Circular Dependencies, especially if you
have classes that have bi-directional communications.  There's definitely
things you can do design-wise to mitigate this, but at a certain point
you're trying to design around something that Python never intended for. I
wish I had a concrete example for you, but unfortunately that's about 200
iterations ago on my current project.  I've since redesigned twice as I
wrapped my head around more of the Python "way", and learned deeper
techniques like Dependency Injection and Inversion of Control that let me
decouple classes more effectively.

The first thing to realize is that an import is not the same as an include,
and we don't have a #pragma once or #ifndef __HEADER_H guard we can set
up.  Technically, of course, we do have the capability, but again with that
approach I'd argue you're trying to force Python into working like C++,
which is not how it's intended.

Python's import system is built around the idea of modules, where a module
maintains some specific standalone functionality, be it audio, a rig
system, etc.  The idea is that each module can stand on its own without
dependencies internal to your overall project.

The second part of the import system is packages, which are basically a
folder of modules, containing a special file called __init__.py.  This file
can have no content, and the whole thing will work as a single module.  If
you do want special content in __init__.py, you can put it in, and that
code will execute when you import the package.  I personally use this
functionality to explicitly expose the classes I intend for use outside the
package.  The key thing to remember with a package is that from Python's
perspective, the whole thing is basically one module once it's loaded.  So,
with that in mind, Packages should also be self-sufficient.

Basically, here's what this means in a nutshell.  When you start splitting
things up, you need to have a solid design already figured out, or you're
going to be in for headaches down the road.  And when I say solid design, I
mean far more solid than most programs you write in other languages.

Take C++, for example.  In C++ I can make two classes that rely on each
other.  Good design? Most likely not.  I surround the headers with
compilation guards to make sure each one only gets loaded into memory once,
and I'm good to go.  That's not to say I have good design, and I'll
probably have headaches down the road as I try to maintain this as a large
project, but it will probably compile and run, which in the prototype stage
is all I'd need.

In Python, there'd be no such luck.  If I put those classes in two separate
files, and then have each file try to import the other, the entire program
won't work, and will give you cryptic messages about Symbols not being
defined.  Why? Well, because the first file will start to load, and will
then try to import the other, which, of course, will lead back to trying to
import the first file.  Python is smart enough to see a loop happening, and
will just cancel both of those imports and try to continue executing,
leading to those errors.

In this case, the easy solution would be to ensure that cross-dependent
classes are in the same file, and then everything will work.  That doesn't
solve the underlying design problem, but it will again make it work.  The
better long-term solution is to redesign your classes so that they don't
rely on each other.

I bring all of this up because it bit me hard early on in my Python
journey, when my instincts were more along the lines of putting everything
in separate files.  Splitting things up will definitely bring any design
flaws you might have to the surface, which, if you're still developing on a
prototype, could be more of a headache than it's worth.  If, on the other
hand, you've already got a working system that you want to break up, what
Justin recommended is exactly right.  Look at your common functionalities,
and put them in their own modules, based on area of operation.  I use a
structure similar to the following:

--Project Root
    --base # This contains global functionality that is specific to my
overall project
    --modules # This is where I put plugins that I'm going to be using, and
is where most of my active development happens. I develop each piece of
functionality in my tools as a standalone plugin that I can include or not
at will.  That's backed by a dynamic loading system (part of settings) that
will check this directory and find available packages. Each of these
modules is separate from the others, and with the exception of one or two
of the base required modules, they don't talk to each other.  They can only
import from base, settings, or utils.  This is actually one I plan on
refactoring further at some point in the future when I'm further along, so
that my base modules will go in a completely different place.  That way, no
Plugin module will rely on another plugin.  I'll probably accomplish this
by moving required modules into a modules package in base.
    --settings # This contains my base settings system to manage options etc
    --ui # Global Project UI functionality. Mostly, this is my main
(non-module-specific) windows.  These windows will call into my modules for
their specific UI elements.
    --utils # Useful functions that I include in any project I use them
in.  I have a library of files I can pull in, separated by functionality.
For example, I have Sequencing Utilities, Functions for Containers,
Namespaces, and so on.  The key with this directory is that the modules
within have no dependencies on anything else internal to my project.  So,
the only things they'd import are Python libraries, maya.whatever, or
PySide stuff.  I can import these from any other scripts except from utils
safely.

The reason I show this is that this is the level of thought you have to put
into it when you start to break things up on a complex project.  I'm
obsessed with clean, easy to read, maintainable code, so as I mentioned
earlier, I like to break things up.  But, when you do that, it comes with
complexity that doesn't tend to rear its ugly head in single file
programs.  With that said, this project has complexity yours might
not--it's a flexible auto-rigger development framework that supports
dynamic modules and abstracts away a lot of the more technical aspects of
the rigging process.  It's currently sitting at close to 15,000 lines of
code across 40-50 files, and I'm anticipating that when I'm "done" with it
that'll be closer to 30-40,000 lines of code.  In short, it's a huge
project, for one person.

So yeah, the methodology for splitting up larger files really depends on
what your code is trying to do, splitting of concerns, etc.

For a simpler setup, here's what one of my modules looks like:

--Module Root
    --blocks # A subdirectory for a module that supports plugins.  This
uses functionality similar to my global module loading.
    --_common.py # This module contains most of my data-handling classes
and functions.
    --_settings.py  # This module hooks into my global settings system and
registers the settings this module uses
    --_ui.py           # This module contains my UI specific code, so the
PySide stuff for this module.
    --__init__.py    # The init file primarily establishes overall module
information for the larger system, and imports the primary externally
accessible Class for this module into the package namespace.

Hope that helps, if you can give some more info on the specifics of your
tool I can give more specifics on the process of re-combining things once
you separate.

On Wed, Mar 18, 2015 at 3:11 PM, <e9554...@gmail.com> wrote:

> Hi yo,
>
> I have a long python script, over 1000 lines now and growing. I was
> thinking would it be wise to start splitting this big program up into
> smaller python files. I have two questions:
>
> 1) would this increase the time it takes to run the script. Say it had to
> load 10 separate python scripts.
>
> 2) how do you do it?, could someone explain this with a simple example?
>
> thanks,
> Sam
>
> --
> You received this message because you are subscribed to the Google Groups
> "Python Programming for Autodesk Maya" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to python_inside_maya+unsubscr...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/python_inside_maya/55b56fbf-0525-427b-b8b8-a21028df5bc5%40googlegroups.com
> .
> For more options, visit https://groups.google.com/d/optout.
>

-- 
You received this message because you are subscribed to the Google Groups 
"Python Programming for Autodesk Maya" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to python_inside_maya+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/python_inside_maya/CAM33%3Da7fEpW2%3Dg-jx%2B9oo5j-1UVLK4w2yNLJa-4p6fNh0G_F4g%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to