Style question: Nicknames for deeply nested objects

G

Gerald Britton

Hi all,

Today I was thinking about a problem I often encounter.  Say that I
have (seems I often do!) a deeply nested object, by which I mean
object within object with object, etc.

For example:

   x = some.deeply.nested.object.method(some.other.deeply.nested.object.value)

Well, that's extreme but I've worked with code approaching that level
of nested-ness.  Now, consider two scenarios:

1. You need to call this thing many times with different arguments, so
you wind up with:

   x = some.deeply.nested.object.method(some.other.deeply.nested.object.value1)
   y = some.deeply.nested.object.method(some.other.deeply.nested.object.value2)
   z = some.deeply.nested.object.method(some.other.deeply.nested.object.value3)

2. You call it inside a loop:

   for item in some_iterable:
       x = some.deeply.nested.object.method(some.other.deeply.nested.object.value)

For one thing, I find the long lines unattractive at best and
error-prone at worst, especially if I also have

   some.other.deeply.nested.object.method

that I might confuse with the first.  To make it look better I might do this:

   _o = some.deeply.nested.object
   _o.method(_o.value)

which is fine, I suppose.

Then, putting on my company hat, I remembered that, from VBA, you could do this:

   with some.deeply.nested.object
       .method(.value)
   end with

I like the structure of this, since the subordinate block can be
indented, which makes it stand out.  Also, it avoids temporary
variables.

So, I was thinking of how to get close to this in Python.  I came up
with two approaches:

1.

   _o = some.deeply.nested.object
   if 1:
       _o.method(_o.value)

The "if 1:" forces me to indent the subordinate code, which sets it
apart from the surrounding code. Note that I cannot just
indent because I feel like it since Python is persnickety about indentation..

2.

for _o in [some.deeply.nested.object]:
       _o.method(_o.value)

The "for..." sets up the iterator and forces me to indent the subordinate code.

As an aside, approach 1 generates less byte-code since approach 2 sets
up loop machinery which you don't really need in this case.

I have a couple of questions:

1. If you had to choose between approaches 1 and 2, which one would
you go for, and why?

2. What other techniques have you used in such a situation?
 
R

rantingrick

[...]
that I might confuse with the first.  To make it look better I might do this:

   _o = some.deeply.nested.object
   _o.method(_o.value)

which is fine, I suppose.

It is very fine. And you "supposed" correctly!

Then, putting on my company hat, I remembered that, from VBA, you could do this:

   with some.deeply.nested.object
       .method(.value)
   end with

I like the structure of this, since the subordinate block can be
indented, which makes it stand out.  Also, it avoids temporary
variables.

Yes it is a good idea! Well forgetting the horrendous VBA syntax this
is. I brought this up many moons back as a feature request: Local
Blocks. Here is how a pythonic local block would look

with this as localvar:
localvar.do_something()

So, I was thinking of how to get close to this in Python.  I came up
with two approaches:

1.

   _o = some.deeply.nested.object
   if 1:
       _o.method(_o.value)

bad idea!

2.

    for _o in [some.deeply.nested.object]:
       _o.method(_o.value)

even worse idea!

I have a couple of questions:

1. If you had to choose between approaches 1 and 2, which one would
you go for, and why?

neither! Stick with the original.
 
R

Roy Smith

Gerald Britton said:
1. You need to call this thing many times with different arguments, so
you wind up with:

   x = some.deeply.nested.object.method(some.other.deeply.nested.object.value1)
   y = some.deeply.nested.object.method(some.other.deeply.nested.object.value2)
   z = some.deeply.nested.object.method(some.other.deeply.nested.object.value3)

I would probably turn that into:

object = some.deeply.nested.object
object.method(object.value1)
object.method(object.value2)
object.method(object.value3)

i.e. make the temporary variable have the exact same name as the last
component of the deeply nested thing you're trying to refactor. If the
scope of use is small and the meaning is obvious from context, sometimes
I'll shorten the name, i.e.

obj = some.deeply.nested.object

or even

o = some.deeply.nested.object

but I tend to avoid doing that. I'd rather be a little more verbose in
preference to being a little more cryptic.
 
S

Steven D'Aprano

Hi all,

Today I was thinking about a problem I often encounter.  Say that I have
(seems I often do!) a deeply nested object, by which I mean object
within object with object, etc.

For example:

   x =
   some.deeply.nested.object.method (some.other.deeply.nested.object.value)

Well, that's extreme but I've worked with code approaching that level of
nested-ness.

Then you're probably living in a state of sin, programming-wise, and you
should stop doing that! You are violating the Law of Demeter. One dot,
good. Two, acceptable. Three is a code smell. Four is a code reek.

The Law of Demeter (more of a guideline than a law, really) says:

If you want to get your dog to walk, call the dog. Don't talk to its
legs, it confuses the dog and doesn't get it anywhere.

http://en.wikipedia.org/wiki/Law_of_Demeter

Another analogy: if you have to pay the paperboy for delivering the
newspaper, you don't let him reach into your pocket, take out your
wallet, open the wallet, take out whatever money he feels like, and put
the wallet back.

http://www.ccs.neu.edu/research/demeter/demeter-method/LawOfDemeter/paper-
boy/demeter.pdf


Despite my comment above, the Law of Demeter is not *really* a dot-
counting exercise, although that's a handy short-cut for detecting
potential problems. It can also apply to other data structures. If you've
every seen C or Pascal code where you have a pointer to a pointer to a
pointer to a pointer to a pointer to a pointer to some data, you've seen
a violation.

Consider your example:

some.other.deeply.nested.object.value

This is very tightly coupled code: the caller, who knows about the object
`some`, needs to know the internal details of not just `some` but also
`other`, `deeply`, `nested`, and `object`. As a basic principle, this is
poor design! Which would you rather deal with?

car.start()

car.ignition.turn(key).connect(car.starter(battery), car.spark_plug)

In particular, using temporary variables merely disguises the problem:

temp = some.other
temp = temp.deeply.nested
x = temp.object.value

Even though you never use more than two dots, you still have tight
coupling. The point of Demeter is not to save dots (they're a renewable
resource) but to reduce tight coupling.
 

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

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top