One class per file?

H

HCB

Hello:

The book "Code Complete" recommends that you put only one class in a
source file, which seems a bit extreme for me. It seems that many
classes are small, so that putting several of them in a file seems
reasonable. I noticed that the decimal.py module in the standard
library has several classes, all of which of course revolve around the
"decimal" topic. Perhaps a better rule of thumb is "one idea per
file." I checked the Python style guide and there seems to be no
mention of this topic. I know this is an elementary question, but what
is the Python way of doing this?

Thanks for your time.
HCB
 
R

Roy Smith

HCB said:
Hello:

The book "Code Complete" recommends that you put only one class in a
source file, which seems a bit extreme for me. It seems that many
classes are small, so that putting several of them in a file seems
reasonable. I noticed that the decimal.py module in the standard
library has several classes, all of which of course revolve around the
"decimal" topic. Perhaps a better rule of thumb is "one idea per
file." I checked the Python style guide and there seems to be no
mention of this topic. I know this is an elementary question, but what
is the Python way of doing this?

Thanks for your time.
HCB

Steve McConnell writes a lot of good stuff. Like most people who write a
lot of good stuff, not everything he writes should be taken as gospel.
With that in mind...

Consider this. You're slogging through some code in a large project trying
to debug a problem when you come upon the line (in pseudo-code):

foo = SomeClass::SomeFunction(bar)

You want to go look at the source for SomeClass. What file do you open to
find it? If you follow the "one class per file" rule, the answer is easy;
it's in SomeClass.xxx!

That being said, the "one class per file" rule is a means to an end. If
your code is written in such a way that it's easy to figure out where the
source for a given class/function/whatever is, then you've done the right
thing. The specifics of how you do that depend on the language you're
using.

In Python (as other posters have pointed out), modules give you a good way
to group logical collections of classes (etc). If you see in some code:

foo = some_module.SomeClass.SomeFunction(bar)

you should instantly know that the source code is in some_module.py.
Perhaps one way to look at this is that Python automatically follows the
"one class per file" rule, except that "class" is crossed out and "module"
written in with crayon.

On the other hand, if you prefer self-abuse, Python gives you plenty of
ways to do that. If you preface all your source files with:

from some_module import *
from some_other_module import *
from yet_a_third_module import *
from another_module import *

and then see

foo = SomeClass.SomeFunction(bar)

you're right back to having no clue what file to open to find the source
for SomeClass. Global namespace is sacred; treat it with respect!
 
B

Bruno Desthuilliers

HCB a écrit :
Hello:

The book "Code Complete" recommends that you put only one class in a
source file,

That's possibly (don't know...) a good advice for C++, and that's
mandatory (at least for 'public' classes) in Java. And that's totally
pointless and counterproductive in Python. Also and FWIW, "this dead
horse as been beaten to hell and back" (IOW : you'll find quite a couple
thread discussing this in this newsgroup's archives).
 
M

Matimus

The book "Code Complete" recommends that you put only one class in a
source file, which seems a bit extreme for me. It seems that many
classes are small, so that putting several of them in a file seems
reasonable. I noticed that the decimal.py module in the standard
library has several classes, all of which of course revolve around the
"decimal" topic. Perhaps a better rule of thumb is "one idea per
file." I checked the Python style guide and there seems to be no
mention of this topic. I know this is an elementary question, but what
is the Python way of doing this?


I'm in a similar situation. I've been reading Robert C. Martins book
"Agile Software Development" and he suggests something similar. I
would highly recommend that book by the way. He also has a couple of
chapters on packaging where he makes some very good points about the
reasoning behind it. Basically that you want to organize your code in
such a way that package dependencies move in the direction of
increasing stability. In this case stability is a metric which is
defined as how likely the code is going to change in the future as
determined by how many packages depend on it as opposed to how many
packages it depends on. He paints a couple of scenarios in which he
groups packages together that _seem_ to be related, only to see that
it results in all of his packages needing to be re-built every time a
change is required. Obviously we don't have to re-build python code,
but it is still useful to organize your code in such a way that you
don't have to constantly re-visit collections of code.

I have actually started using his suggestion and have been putting one
class per file. When one class depends on another I include it with a
"from x import X". This makes it very easy to determine the
dependencies (well, the non-abstract dependencies anyway, interfaces
are a different story*). Then I put all of my classes together in a
package, and make the "public" classes visible on a package level by
importing them into the package __init__ module.

With that being said, If I made a class that was _only_ used by one
other single class, and it was clear that it would never be made
visible outside of that file, I would certainly put it in the same
file as that class. Heck, you might even refactor your code and
determine at that time that some piece of code is only used by one
other piece. It is much easier to put code together after the fact
than it is to separate it, especially later in the game.

My advice: don't knock it until you try it. I think my code has
improved quite a bit since taking this advice. It can be much more
difficult to determine which classes to package together until much
later in the development cycle. One thing that can help is to find an
IDE that helps you to easily switch between files. I use WingIDE, but
I have even used vim with a tags file and it wasn't terrible. I
wouldn't call it a hard rule, but at the same time I wouldn't just
ignore it. Give it a shot and adapt your development technique to see
what works best for you.

Example:

[mypackage/__init__.py]
__all__ = ["X"]

from .x import X
[end mypackage/__init__.py]

[mypackage/x.py]
from .y import Y

__all__ = ["X"]

class X(object):
# code - using Y hopefully
[end mypackage/x.py]

[mypackage/y.py]
__all__ = ["Y"]

class Y(object):
# code
[end mypackage/y.py]


Matt



* Interfaces in python represent an implicit dependency. The Zen of
Python states: "Explicit is better than implicit". I plan to
experiment with the ABC module in python 2.6/3.0. I want to make my
interfaces explicitly defined, but on the other hand I still want it
to be easy for people who use my code to duck-type. This _seems_ to be
possible since you can .register a class with an interface after it
has been created, but I'm not sure that it is very pythonic to
explicitly check for an interface every time it is used. It would seem
silly however to import an interface into a file where it isn't
explicitly used just to document the dependency. If anybody has
pointers let me know.
 
T

Tim Rowe

2008/9/29 Roy Smith said:
That being said, the "one class per file" rule is a means to an end.

Although in some languages, the "end" is "code that runs". But Python
is not Java...
 
H

HCB

To answer Ben Finney's questions:

The "Put one class in one file" statement is made in "Code Complete 2"
page 771.

HCB
 
L

Lawrence D'Oliveiro

In message
HCB said:
The book "Code Complete" recommends that you put only one class in a
source file ...

That would only apply to languages like C with no namespace control.
 
B

Bruno Desthuilliers

Lawrence D'Oliveiro a écrit :
In message


That would only apply to languages like C with no namespace control.

classes in C ?-)

OTHO, 'one class per file' is a standard idiom in Java and IIRC in C++
(which both have namespaces one way or another)
 
G

greg

Bruno said:
OTHO, 'one class per file' is a standard idiom in Java and IIRC in C++
(which both have namespaces one way or another)

In Java you don't get a choice, because the compiler
assumes a class can be found in the correspondingly
named file.

While C++ has namespaces, they don't have any defined
relationship to source files, so they don't help you
find which file something is defined in.
 
B

Bruno Desthuilliers

greg a écrit :
In Java you don't get a choice, because the compiler
assumes a class can be found in the correspondingly
named file.

For public classes only.
While C++ has namespaces, they don't have any defined
relationship to source files, so they don't help you
find which file something is defined in.

Nope. But IIRC, one-class-per-file helps saving on compile/link time. A
problem we don't have with dynamic languages !-)
 
A

Aaron \Castironpi\ Brady

greg a écrit :



For public classes only.




Nope. But IIRC, one-class-per-file helps saving on compile/link time. A
problem we don't have with dynamic languages !-)

I think that one goes in the 'handcuffs' category. You are free to
adopt the idiom if you want, aren't required to. (Though doesn't the
freedom handcuff later users of your code?)

Besides, it's not always clear what method in an inheritance tree is
getting called, so 'one class per file' doesn't guarantee you can find
an operation.

With 'exec( "class %s:\n..." )' statements and 'def f(): / class X: /
return X' statements, you don't even know all your classes at
startup. And not to mention 'namedtuple's certainly make the idiom
impractical in at least corner cases.

(But, when you need code and can't find discipline, maybe 'inside-the-
box' restrictions can bring your goal closer, no?)
 
G

greg

Bruno said:
Nope. But IIRC, one-class-per-file helps saving on compile/link time. A
problem we don't have with dynamic languages !-)

That's mostly true. Although I've noticed that if I have
a very large .py file, it can take a noticeable number
of moments to regenerate the .pyc after I've changed
something.
 
T

Tony Meyer

[HCB]
IMO this is a misunderstanding (by the author). In Python, a file is
not equivalent to a class, it is equivalent to a module. A module
might contain a single class or many, just as a class might contain a
single method or many. You can tell that files are not classes,
because you have have variables, methods, etc at the module level,
outside of class definitions.

If all of the module's functionality is in a single class, then of
course the file only contains one class. If the module's
functionality is split over many classes (or no classes at all), then
the file will not contain exactly one class. Rigidly putting every
class in a separate file would mean that the next level higher than
classes wouldn't be modules, it would be packages.

[Roy Smith]
Consider this. You're slogging through some code in a large project
trying
to debug a problem when you come upon the line (in pseudo-code):

foo = SomeClass::SomeFunction(bar)

You want to go look at the source for SomeClass. What file do you
open to
find it? If you follow the "one class per file" rule, the answer is
easy;
it's in SomeClass.xxx!

Alternatively, most IDEs will let you go to the source very simply.
If you don't have that facility available, then you just do "import
X;print X.__file__".

Cheers,
Tony
 
B

Bruno Desthuilliers

greg a écrit :
That's mostly true. Although I've noticed that if I have
a very large .py file, it can take a noticeable number
of moments to regenerate the .pyc after I've changed
something.

Indeed - and "very large" files are a maintainance nightmare anyway.

The point was that with C++, when you edit a file, you not only have to
recompile this file, you *also* have to relink *everything* depending on
what's defined in this file. So the saving is mostly on on (re)linking
time.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,780
Messages
2,569,611
Members
45,282
Latest member
RoseannaBa

Latest Threads

Top