detecting containers during object introspection

Steven Taschuk staschuk at telusplanet.net
Tue Jul 22 02:42:04 CEST 2003


Quoth David C. Fox:
> Is there a reasonable way to tell whether a variable is a container 
> (either a mapping or a sequence) and whether it is a mapping or a 
> sequence?  [...]

Depends what you mean by "reasonable".

In the usual scenario, there's a specific facility which you want
to make use of, and you can either (a) just try to use it, and
catch the relevant exception (called Easier to Ask Forgiveness
than Permission, or EAFP), or (b) do some kind of check beforehand
for whether the facility exists (called Look Before You Leap, or
LBYL).

Your example of trying iter(foo) and catching the possible
TypeError fits into such approaches.

  [...]
> Wouldn't it be helpful to have an abstract base class Sequence which 
> didn't add any actual attributes or methods, but which someone writing a 
> sequence class could include as a base class, as a sort of unenforced 
> promise that the sequence operators were supported?

Consider a DNS name -> address mapping whose __getitem__ actually
queries the DNS.  (Ignore the fact that this could be better done
with a function -- assume we have a function which expects a
dict-like object and we want to use it on the DNS.)  Such an
object could not reasonably implement __len__.  Should it falsely
imply that it does (by inheriting from an abstract class Mapping)
or falsely imply that it's not a mapping (by not inheriting from
Mapping)?

Such cases -- those in which for conceptual or pragmatic reasons
it is infeasible or undesirable to implement all of an interface
-- are not uncommon.

Also, there's the idea of YAGNI (You Ain't Gonna Need It):
implement just what you discover you actually need, not what you
think you might need in the future.  In particular, when writing a
dict-like object, write just as much of the interface as the
contexts in which you want to use that object require, not more.
Implementing the entire interface just for completeness is
(according to YAGNI) a waste of valuable developer time, until you
discover you really do need the complete interface.  Adherents of
this idea won't like the notion of having to make a promise
(enforced or not) that they've implemented the entire interface.

> Why am I asking this question?
> 
> I'm trying to write a regression test for a class which pickles 
> dictionaries of attributes to store and retrieve instances.  The 
> dictionary includes a version number, and the class has a system for 
> updating out-of-date instances to the current version.
> 
> As part of the test, I need to be able to compare the structure of the 
> two dictionaries to see if the developer has modified their structure 
> but forgotten to increment the current version.  Values in the 
> dictionary may be unknown objects, in which case I just want to compare 
> their types.  However, the values may also be sequences or mappings 
> themselves, in which case I want to recursively compare their 
> elements/values.

Interesting.  So you have an example of a standard dictionary for
a given version number, and you want to compare the structure of
the actual dictionary to the standard example dictionary.  Is that
right?

Are your developers actually using dict-like and list-like objects
instead of real dicts and lists?  If not, isn't the type-check for
unknown objects enough?  (If they do start using work-alikes
instead of the real thing, the test will fail, but YAGNI, right?
You can fix it if it ever happens, at worst by adding support for
the specific kinds of work-alike the developers want to use.  Only
if they want to use a wide variety of work-alikes do you really
need tests for abstract sequenceness and mappingness.)

-- 
Steven Taschuk                            staschuk at telusplanet.net
"Our analysis begins with two outrageous benchmarks."
  -- "Implementation strategies for continuations", Clinger et al.





More information about the Python-list mailing list