[Python-ideas] 80 character line width vs. something wider

Steven D'Aprano steve at pearwood.info
Wed May 20 01:54:46 CEST 2009


On Wed, 20 May 2009 03:53:33 am Aaron Rubin wrote:

> On Tue, May 19, 2009 at 10:14 AM, David Stanek <dstanek at dstanek.com> 
wrote:

> > >   Object Oriented:  Because it is not functional-style
> > > programming, but instead OO, you have to give defintion as to
> > > what object type you are using before using it.  This makes
> > > definitions and usage longer than in functional
> > > programming (when 80 character widths were invented).

That's a red-herring. Functional programming is quite capable of 80+ 
character lines too:

FetchFrameGrabber(ExtractAbstract(GetFrameSource(GetHardware(MakePhazeMonkey()))))

> > >  PhazeMonkey.Hardware.FrameSource.Abstract.Framegrabber is an
> > > example

Such a deeply hierarchical call is poor technique regardless of what 
each level represents: classes, modules, function calls, or something 
else. It is reasonable for parts of your code that understand 
PhazeMonkeys to know that a PhazeMonkey has Hardware, but it is 
completely unreasonable for that same section of code to rely on the 
existence of PhazeMonkey.Hardware.FrameSource.Abstract.Framegrabber. 
That sort of tight coupling between the top of the hierarchy and the 
bottom defeats the purpose of encapsulation.

I see that in a later post, Aaron writes:

"The Law of Demeter applies to objects referenced second-hand.  The 
class name given is an example of a hierarchy of modules, not one class 
reaching through a second class to get at the class members it uses."

That's an irrelevant objection. Demeter is a design principle: it 
applies to classes, namespaces, military units, corporate business 
units or any hierarchical structure. Of all the military blunders in 
World War I, the one they never made was for General Haig to call up 
the front line and say "Hello, let me speak to Private Baldrick. 
Baldrick, I want you to take your rifle and point it due east and use 
your index finger to pull the trigger at 1700 hours precisely."

Besides, modules *are* objects:

>>> import math
>>> isinstance(math, type(math))
True



> > If you are using more than 5 or 6 levels of indentation you may be
> > doing something wrong. I would guess that your methods are too
> > complex or maybe you are violating the SRP.
>
> See below for code example

Namely:

> class a(object):
>     def method1(simulation=False):
>         try:
>             if simulation:
>                 for x in range(10):
>                     if x>5:
>                         try:
>                             # here might begin some actual math, with
> two or three more levels of logic, interfacing with other libraries
> such as NumPy, etc. where you might need specific error handling
>         except CustomError:
>             # customer error handling
>
> i.e. the logic code *started* at the 7th indentation level.  But I'm
> sure you can find plenty of examples where it might be more.

Far too complex in my opinion. That's best refactored into separate 
methods or functions. For example:

class A(object):
    def simulated_method1(self):
        self._common()
    def method1(self):
        try:
            for x in range(5):
                self._metha(x)
            for x in range(5, 10):
                self._methb(x)
            self._common()
        except CustomError:
            self._fallback()
    def _metha(self, x):
        """Return function(x), for 0 <= x < 5."""
        assert 0 <= x < 5
    def _methb(self, x):
        """Return function(x), for 5 <= x < 10."""
        assert 5 <= x < 10


That's (possibly?) longer code, but it comes in single-thought-sized 
chunks. Each method does one thing, rather than wrapping a whole bunch 
of loosely-related functionality into a single method.

Another advantage is that it reduces the amount of test code you need. 
Instead of what could be an exponentially large number of possible 
paths through method1(), each individual method only has one or 
possibly two paths that need testing.


> > I'd like to see an example of your variable names. I don't use
> > hungarian notation and my name are usually under 10 characters.
>
> I gave an example already in the snippet you quoted.

Namely "hasInstrumentControllerPhaseDither".

Well, your naming conventions are yours, naturally, and you can use any 
convention you like, but I can't imagine ever willingly using a name 
like that. I would suggest that if you need to distinguish 
hasInstrumentControllerPhaseDither from (say) 
hasInstrumentWidgetPhaseDither, hasScannerControllerPhaseDither and 
hasInstrumentControllerAmplitudeDither in the one function, your 
function is far too complex, doing too many things, and should be split 
up into smaller single-purpose functions.


> > > 5) Monitors are getting bigger, wider, cheaper.  This allows us
> > > to have two
> > > files, side-by-side on a screen that are *both* 120 character
> > > width (or even wider), without changing font sizes.

True enough for those editing on large monitors, AND who like viewing 
multiple files side-by-side. (Personally, I very rarely do, and then 
only when comparing two files line-by-line.) But have pity on those 
using a EEE or similar machine.

Besides, if you can fit two 120-char wide files on screen, you can fit 
three 80-char wide files on screen.


For what it's worth, although I've never needed to interoperate with 
code using .NET style guidelines or written a 100 KLOC program, I 
generally have very little difficulty in keeping 90% of my code below 
*seventy* characters per line, including four-space indents, and 
virtually all of the rest below eighty. The short line-length 
encourages me to refactor my code so that every function does one thing 
and one thing only.


-- 
Steven D'Aprano


More information about the Python-ideas mailing list