Small hack to help interactively identify components of an object

Ken Manheimer (ken.manheimer@nist.gov)
Fri, 16 Sep 94 13:29:00 EDT

I tend to rely on the machine rather than my memory for remembering
things about the language and language environments that i use.
That's why (or is it the other way around?) i take so well to
interpreted languages like python - when i need to remember what
sequence operations are available to me, i do:

>>> [].__methods__
['append', 'count', 'index', 'insert', 'remove', 'reverse', 'sort']

I'd like to see this capability generalized to be more comprehensive.
So, for example, even the attribute-less objects like strings and
tuples can be investigated this way. I figure it would not be hard
(given good knowledge of the system) to make a 'features()' builtin,
or something, that unfailingly returns a list of the methods
associated, even implicitly, with an object. Ie, you would see things
like __getitem__ and __length__ listed for strings and tuples, instead
of getting "TypeError: attribute-less object" when you try to do a
'().__methods__'.

Along a related line, i often need to determine the distinctive
components of an object, in order to recall how to do something
particular to/with it. For a trivial example, that would be the
specific keys of a module. More elaborately, that would be the data
and method components of a class, or the names (obj.func_code.co_names)
of a function object. This is as opposed to the __methods__ or
__members__ of these complex types, which stay constant from one
instance to the next.

In trying to express this desire more clearly, for my own sake, i
hacked together the following little module, which implements a
'components()' function. You apply this function to any object, and
it tries to return an informative list of distinctive components of
that object.

There may be better choices for representing one object or another,
and the code has not been extensively tested. However, you may find
it useful, if you, like me, like to poke around to recall answers,
instead of relying solely on your memory. (I'd be happy to see more
tools like this for exploring the interpreted environment, in case
anyone has some to share...)

Ken
ken.manheimer@nist.gov, 301 975-3539

# Module 'components'.

def components(obj):
# Return a list of the salient components of an object, where 'salient'
# is loosely defined to mean the attributes and methods of the object that
# distinguish it from other objects of the same type. Ie, for a function,
# it would be the distinctive names in the function code
# (func.func_code.co_names).
#
# Depends on the settings of the 'types' dict, as established by the
# 'populateTypes' function.

them = [] # accumulator

if type(obj) == types['class']:
# Return the immediate components of the class, plus its' bases.
if obj.__bases__:
them = components(obj.__bases__)
return dir(obj) + them

elif type(obj) == types['instance']:
# Return the data of the instance, plus the class components.
return dir(obj) + dir(obj.__class__)

elif type(obj) == types['instMethod']:
# Return the components of the methods' function:
return components(obj.im_func)

elif type(obj) == types['function']:
# Return all the names within the function - would like to trim it down
# to local vars, but i think the local dict is not established until
# invocation time...
return obj.func_code.co_names

elif type(obj) == types['tb']:
return components(obj.tb_frame)

elif type(obj) == types['frame']:
return obj.f_locals

else:
# Not one of the covered complex types - try for __dict__, __members__,
# and __methods__, or any combination:
for aspect in '__dict__', '__members__', '__methods__':
try:
them = them + eval('obj.' + aspect)
except (AttributeError, TypeError):
pass
return them

types = {}
def populateTypes():
# Creates type entries for fundamental complex types, in dict 'types'.

import sys

# Create and register traceback and frame types:
try: raise 'x'
except: tb = sys.exc_traceback
types['tb'] = type(tb)
types['frame'] = type(tb.tb_frame)
types['code'] = type(tb.tb_frame.f_code)

# Create and register executable types:
def func(): pass
types['function'] = type(func)
class cl:
def meth(): pass
types['instMethod'] = type(cl.meth) # unbound meth same type as inst meth
clinst = cl()
types['instance'] = type(clinst)
types['class'] = type(cl)

populateTypes() # Populate the 'types' dict.