Encapsulation in Python

Rick Johnson rantingrickjohnson at gmail.com
Fri Mar 11 21:56:04 EST 2016


On Friday, March 11, 2016 at 9:48:22 AM UTC-6, Ian wrote:
> On Thu, Mar 10, 2016 at 5:45 PM, Rick Johnson wrote:
> > Many times, i would have preferred to define my module space
> > across multiple files, multiple files that could share state
> > without resorting to the yoga-style "import contortions",
> > and/or the dreaded "circular import nightmares" that plague
> > our community today.
>
> Sounds to me like you're blaming Python for your own poor design.
> Possible solutions:
>
> 1) Don't do that. If your module is too big for one file, then it's
> likely poorly organized and doesn't all belong in one module anyway.

Apparently you've missed the many heated arguments between
Chris Angelico and myself concerning the size of source
files. I have *ALWAYS* taken the position that source files
should be kept as small as possible, but that position is
more relevant in Python, were modules are defined by a
single source file. I don't take the Java position that a
module can only contain one class, but i like to keep the
number of classes (IN MY SOURCE FILES) small.

At run-time, i don't care how large a "module namespace" may
be. Sometimes a module namespace will be small, with only a
few exposed symbols, but sometimes, a module namespace will
expose thousands of symbols. The size of a "run-time module"
is irrelevant in most languages, but here in Python, were
module namespace is defined by the contents of *ONE SINGLE
SOURCE FILE*, the whole ballgame is changed. If i need to
create a module that contains many exposed symbols in
Python, i'm forced to do one of the following:

  (1) Write all my code in a single *GIGANTIC* file...

or

  (2) Create one file that will be the "mother-ship module",
  and N files that will be the "satellite modules", and from
  inside the mother-ship, import all the symbols from all
  the satellites. Ha, but in reality, it's not that simple,
  because state does not "magically" travel between modules!

    ##########
    # foo.py #
    ##########
    FOO_SHARED_STATE = "foo"
    import _fooSatilite1
    _fooSatilite1.FOO_SHARED_STATE = FOO_SHARED_STATE
    from _fooSatilite1 import *
    import _fooSatilite2
    _fooSatilite2.FOO_SHARED_STATE = FOO_SHARED_STATE
    from _fooSatilite2 import *
    print 'This is the mother-ship called foo'
    ...

    ####################
    # _fooSatilite1.py #
    ####################
    from _fooConstants import *
    print 'This is foo-fighter1, reporting for duty'
    print FOO_SHARED_STATE
    ...

    ####################
    # _fooSatilite2.py #
    ####################
    from _fooConstants import *
    print 'This is foo-fighter2, reporting for duty'
    print FOO_SHARED_STATE
    ...

But i find both to be absurd. Writing all code in a single
file might be fine for a toy module that contains a handful
of functions or classes or vars, but once we start creating 
anything in the "professional size range", it will become 
an "editing nightmare" of epic proportions!

But option two is no better, because once we cut and paste
portions of the code into satellite files, we lose the
ability to "easily share state". Then we're forced to start
"hacking at the weeds" with import contortions and monkey
patches, all the while, fearing the dreaded circular import.


NO, THIS IS INSANITY!  WHAT WE NEED IS AN OPTION 3!

 (3) Allow a programmer to define module space at will

    ##########
    # foo.py #
    ##########
    module foo
    FOO_SHARED_STATE = "foo"
    print 'This is the mother-ship called foo'
    ...

    ####################
    # _fooSatilite1.py #
    ####################
    module foo
    print 'This is foo-fighter1, reporting for duty'
    print FOO_SHARED_STATE # NO MP REQUIRED!
    ...

    ####################
    # _fooSatilite2.py #
    ####################
    module foo
    print 'This is foo-fighter2, reporting for duty'
    print FOO_SHARED_STATE # NO MP REQUIRED!
    ...

No need for import contortions, no need for monkey patching,
but most importantly, no need for professional programmers
to feel as though they are penchant little children, who
need their "module diapers" wrapped for them by mommy
Python, who, after popping one too many "mother's little
helpers", has a nasty habit of wrapping our diapers too damn
tight -- it's high time for these py-babies to go commando,
baby!

> 2) Clearly define which module is to be imported first. Then follow
> it. When module b is imported, it should import module a. When module
> a is imported, it *shouldn't* import module b until it's defined all
> of its own members first. If module a depends on anything from module
> b at import time, then refactor so it doesn't. This doesn't require
> "contortions".

That sounds simple in theory, but it breaks down quickly
when you create "professional sized programs"

> 3) Just put all your damn shared state in a class. There's nothing
> stopping you from spreading out your class method definitions over
> multiple files if you really want to, and it solves your import issue
> by allowing everything to be imported before you even begin to set up
> the shared state.

Your words tell me that you have not written anything substantial.

> And you're suggesting that public attributes and properties do not
> expose an interface? Rubbish.

No, what i'm suggesting, is that, from the POV of a dir
listing, they *ALL* look the same.

> I don't care how easy they are to *create*. Code is read much more
> often than it is written, and excessive boilerplate damages
> readability.

I understand the importance of readability, but is usability
of no concern to you? In fact, i would argue that, when
*ROCK SOLID* professional interfaces are combined with
proper documentation and strict adherence to style guides,
there is no need to read source code.

The only person who should be reading source, is a
maintenance programmer. And if the code was designed
properly, his job would be mostly just adding a feature here
or there, or repairing a small bug here or there -- any code
monkey can do that!

> >   (2) Properties and attributes encourage vague naming
> >   schemes. When i read code, i find the code more
> >   comprehensible when the symbols give me clues as to what
> >   is going on. So if i read code like: `re.groups`,
> >   befuddlement sets in. What is "groups"? A function
> >   object? An attribute? "getCapturingGroupCount" would be a
> >   better name (but that's semantics)
>
> Actually, it would be wrong. Match.groups doesn't return a count.

Who said anything about match objects?

############################################################
# BEGIN INTERACTIVE SESSION
############################################################
py> import re
py> prog = re.compile(r'(Ian) (\w+)')
py> prog?
['findall', 'finditer', 'flags', 'groupindex', 'groups', 'match', 'pattern', 'scanner', 'search', 'split', 'sub', 'subn']
py> prog.groups
2
py> prog.groups?type
<type 'int'>
############################################################
# END INTERACTIVE SESSION
############################################################

Looks like an integer to me. (Well, i'll admit, first you'll
have to parse my mini-language syntax, but it's highly
intuitive -- more than a Python dir listing, i'll tell you
that much!)

> >   In pure OOP,  methods
> >   are the only communication meduim, so we're more likely to
> >   write "getBlah" and "setBlah", and use verbs for
> >   procedural names -- these naming conventions are more
> >   comprehensible to the user.
>
> Good names for methods are *descriptive* verb phrases. Good names for
> attributes and properties are *descriptive* nouns or noun phrases. Do
> you really believe that verbs are somehow inherently easier to
> understand?

When i see "get_width" or "set_width", i know that the first is
returning the width, and the second will set the width. And
anyone with an IQ over room temperature, can surmise that
set_width takes at least one argument.

However, when i see "width", how the hell am i supposed to
know if it is a read-only attribute, a property, or a method?
Must i read source code just to find this out? Sure, if the
author included a useful doc string, i could pull it up, but
to me, this is a giant waste of time. Self documenting names
are the *KEY* to short learning curves.



More information about the Python-list mailing list