18

I'm freshly coming to the Python world after years of Java and PHP. While the language itself is pretty much straightforward, I'm struggling with some 'minor' issues that I can't wrap my head around — and to which I couldn't find answers in the numerous documents and tutorials I've read this far.

To the experienced Python practitioner, this question might seem silly, but I really want an answer to it so I can go further with the language:

In Java and PHP (although not strictly required), you are expected to write each class on its own file, with file's name is that of the class as a best practice.

But in Python, or at least in the tutorials I've checked, it is ok to have multiple classes in the same file.

Does this rule hold in production, deployment-ready code or it's done just for the sake of brevity in educative-only code?

12

Is it ok to have multiple classes in the same file in Python?

Yes. Both from a philosophical perspective as well as a practical one.

In Python, modules are a namespace that exist once in memory.

Say we had the following hypothetical directory structure, with one class defined per file:

                    Defines
 abc/
 |-- callable.py    Callable
 |-- container.py   Container
 |-- hashable.py    Hashable
 |-- iterable.py    Iterable
 |-- iterator.py    Iterator
 |-- sized.py       Sized
 ... 19 more

All of these classes are available in the collections module and (there are, in fact, 25 in total) defined in the standard library module in _collections_abc.py

There are a couple of issues here that I believe makes the _collections_abc.py superior to the alternative hypothetical directory structure.

  • These files are sorted alphabetically. You could sort them in other ways, but I am not aware of a feature that sorts files by semantic dependencies. The _collections_abc module source is organized by dependency.
  • In non-pathological cases, both modules and class definitions are singletons, occurring once each in memory. There would be a bijective mapping of modules onto classes - making the modules redundant.
  • The increasing number of files makes it less convenient to casually read through the classes (unless you have an IDE that makes it simple) - making it less accessible to people without tools.

Are you prevented from breaking groups of classes into different modules when you find it desirable from a namespacing and organizational perspective?

No.

From the Zen of Python , which reflects the philosophy and principles under which it grew and evolved:

Namespaces are one honking great idea -- let's do more of those!

But let us keep in mind that it also says:

Flat is better than nested.

Python is incredibly clean and easy to read. It encourages you to read it. Putting every separate class in a separate file discourages reading. This goes against the core philosophy of Python. Look at the structure of the Standard Library, the vast majority of modules are single-file modules, not packages. I would submit to you that idiomatic Python code is written in the same style as the CPython standard lib.

Here's the actual code from the abstract base class module. I like to use it as a reference for the denotation of various abstract types in the language.

Would you say that each of these classes should require a separate file?

class Hashable:
    __metaclass__ = ABCMeta

    @abstractmethod
    def __hash__(self):
        return 0

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Hashable:
            try:
                for B in C.__mro__:
                    if "__hash__" in B.__dict__:
                        if B.__dict__["__hash__"]:
                            return True
                        break
            except AttributeError:
                # Old-style class
                if getattr(C, "__hash__", None):
                    return True
        return NotImplemented


class Iterable:
    __metaclass__ = ABCMeta

    @abstractmethod
    def __iter__(self):
        while False:
            yield None

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Iterable:
            if _hasattr(C, "__iter__"):
                return True
        return NotImplemented

Iterable.register(str)


class Iterator(Iterable):

    @abstractmethod
    def next(self):
        'Return the next item from the iterator. When exhausted, raise StopIteration'
        raise StopIteration

    def __iter__(self):
        return self

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Iterator:
            if _hasattr(C, "next") and _hasattr(C, "__iter__"):
                return True
        return NotImplemented


class Sized:
    __metaclass__ = ABCMeta

    @abstractmethod
    def __len__(self):
        return 0

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Sized:
            if _hasattr(C, "__len__"):
                return True
        return NotImplemented


class Container:
    __metaclass__ = ABCMeta

    @abstractmethod
    def __contains__(self, x):
        return False

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Container:
            if _hasattr(C, "__contains__"):
                return True
        return NotImplemented


class Callable:
    __metaclass__ = ABCMeta

    @abstractmethod
    def __call__(self, *args, **kwds):
        return False

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Callable:
            if _hasattr(C, "__call__"):
                return True
        return NotImplemented

So should they each have their own file?

I hope not.

These files are not just code - they are documentation on the semantics of Python.

They are maybe 10 to 20 lines on average. Why should I have to go to a completely separate file to see another 10 lines of code? That would be highly impractical. Further, there would be nearly identical boilerplate imports on each file, adding more redundant lines of code.

I find it quite useful to know that there is a single module where I can find all of these Abstract Base Classes, instead of having to look over a list of modules. Viewing them in context with each other allows me to better understand them. When I see that an Iterator is an Iterable, I can quickly review what an Iterable consists of by glancing up.

I sometimes wind up having a couple of very short classes. They stay in the file, even if they need to grow larger over time. Sometimes mature modules have over 1000 lines of code. But ctrl-f is easy, and some IDE's make it easy to view outlines of the file - so no matter how large the file, you can quickly go to whatever object or method that you're looking for.

Conclusion

My direction, in the context of Python, is to prefer to keep related and semantically similar class definitions in the same file. If the file grows so large as to become unwieldy, then consider a reorganization.

  • 1
    Well, while I understand, thanks to the code you submitted that it's OK to have multiple classes in the same file, I can't find the argument very convincing. For example, it was very common in PHP to have an entire file with only code similar to this: class SomeException extends \Exception {} – Olivier Malki Jan 4 '16 at 17:00
  • 3
    Different communities have different coding standards. Java people look at python and say "why does it allow multiple classes per file!?". Python people look at Java and say "why does it require each class have its own file!?". It is best to follow the style of the community you are working in. – Steven Burnap Jan 4 '16 at 17:27
  • I'm confused here too. Apparently I misunderstood some things about Python with my answer. But would one apply "flat is better than nested" to add as many methods as possible to a class? In general I would think the principles of cohesion and SRP still apply to a module favoring modules that provide classes which relate closely to each other in functionality (though perhaps very well more than one class since a module models a coarser package concept than a single class), especially since any module-scope variables (though hopefully avoided in general) would increase in scope. – user204677 Jan 5 '16 at 12:34
  • 1
    The Zen of Python is a list of principles that are in tension with each other. One might respond in agreement with your point with, "Sparse is better than dense." - which immediately follows, "Flat is better than nested." Individual lines from The Zen of Python can easily be misused and taken to extremes, but taken as a whole, it can aid in the art of coding and finding common ground where reasonable people may otherwise disagree. I don't think people would consider my code examples dense, but what you describe sounds very dense to me. – Aaron Hall Jan 5 '16 at 13:48
  • Thank you Jeffrey Albertson/Comic Book Guy. :) Most users of Python shouldn't use the (double-underscore) special methods, but they do allow a core designer/architect to engage in meta-programming to make custom use of operators, comparators, subscript notation, contexts, and other language features. So long as they do not violate the principle of least surprise, I think the harm to value ratio is infinitesimal. – Aaron Hall Jan 5 '16 at 15:36
4

When structuring your application in Python, you need to think in terms of packages and modules.

Modules are about the files you are talking about. It is fine to have a bunch of classes within the same module. The aim is that all the classes within the same module should serve the same purpose/logic. If the module goes too long then think of subdividing it by redesigning your logic.

Do not forget to read from time to time about Index of Python Enhancement Proposals.

2

The real answer to this is general, and not dependent on the language that's used: What should be in a file does not primarily depend on how many classes it defines. It depends on logical connectedness, and on complexity. Period.

Thus, if you have a few very small classes that are highly interconnected, they should be bundled into the same file. You should split out a class if it's either not tightly connected to another class, or if its too complex to be included with another class.

That said, the one-class-per-file rule is usually a good heuristic. However, there are important exceptions: A small helper class that is really just the implementation detail of its sole user class should generally be bundled into that user class's file. Likewise, if you have three classes vector2, vector3, and vector4, there is likely little reason to implement them in separate files.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.