Class introspection and dynamically determining function arguments
Mike C. Fletcher
mcfletch at rogers.com
Sat Jan 22 13:44:06 EST 2005
Bengt Richter wrote:
>On Fri, 21 Jan 2005 20:23:58 -0500, "Mike C. Fletcher" <mcfletch at rogers.com> wrote:
>>>On Thu, 20 Jan 2005 11:24:12 -0000, "Mark English" <Mark.English at liffe.com> wrote:
>Does the BasicProperty base class effectively register itself as an observer
>of subclass properties and automatically update widgets etc., a la Delphi
>data-driven visual components? I've thought of doing a light-weight form
>extension class that would use a text (maybe CSV) definition to control
>contruction, and easy programmatic manipulation by python of the definition
>parameters, like a stripped-down version of the text view of Delphi forms.
>It could also be done via Tkinter, to prototype it. It would be interesting
>to allow dragging widgets and edges around in Tkinter and round-trip the parameter
>changes automatically into the text representation. A little (well, ok, a fair amount ;-)
>further and you'd have a drag-n-drop GUI design tool. But don't hold your breath ;-)
BasicProperty itself doesn't register as an observable/observer,
BasicProperty is the lowest-level of the software stack, so it allows
you to override and provide notification (e.g. using PyDispatcher) on
property-setting. ConflictSolver (old project to create a room
scheduler) used that to do automatic updating of widgets in the wxPython
UI based on Model changes (though I don't remember if it was
per-property or per-object). My goal for the wxoo project was to
provide hooks in the wxPython GUI designers for dropping in property
sheets and/or property-aware controls such that you would have the
equivalent of "data aware" controls in VB or Access (keeping in mind
that BasicProperty properties can also represent fields in database rows).
The VRML97 field class in OpenGLContext does notifications for every
set (using PyDispatcher), btw. It's a little more limited in its
scope (focus on 3D data-types), but the effect is what allows the
scenegraph to cache and then rebuild its internal rendering
structures with very low overhead.
>>Anyway, if you aren't interested in BasicProperty for this task; another
>>project on which I work, PyDispatcher provides fairly robust mechanism
>>(called robustApply) for providing a set of possible arguments and using
>>inspect to pick out which names match the parameters for a function in
>>order to pass them in to the function/method/callable object. That
>>said, doing this for __init__'s with attribute values from an object's
>>dictionary doesn't really seem like the proper way to approach the problem.
>Sounds like a workaround for parameter passing that maybe should have been
Not as such, that is, not a workaround, and it shouldn't be keyword
based ;) .
The problem with using keyword-based passing is that every method needs
to be written with this awareness of the keyword-handling structures.
You spread pointless implementation details throughout your codebase.
PyDispatcher lets you write very natural functions for dealing with
events without having every function use **named parameters.
I've now written quite a few such systems, and I'm currently balanced
between two approaches; that taken in PyDispatcher (define only natural
parameters, have the system figure out how to deliver them to you), and
that taken in OpenGLContext (define an event-class hierarchy which
encapsulates all information about the events).
The PyDispatcher approach is nice in that it makes simple things very
simple. You want access to the "lastValue" parameter in the
"environment" of the event and nothing else, you define your function
def handler( lastValue ):
print 'got last value', lastValue
which works very nicely when you're early in the development of a
system, or are linking multiple systems. There's no need to do all
sorts of extra work defining event hierarchies, you can often leave
given handlers entirely alone during refactoring if they aren't dealing
with the changed properties in the event-environment.
The OpenGLContext approach is more appropriate when you have a large
system (such as OpenGLContext), where defining an event class is a
trivial task compared to the total system expenditure. It allows for
such things as putting methods on the event objects to make debugging
easy, and providing common functionality. It starts to show it's worth
when you start needing to reason about the phenomena of events
themselves, rather than just about the phenomena the events represent
(e.g. when you need to cache, delay, or reorder events).
The named argument passing approach has the disadvantage that every
function must be written with knowledge of that use of named arguments:
def handler( lastValue, **named ):
print 'got last value', lastValue
when using such systems in the past I've often wound up with errors deep
in an application where some seldom-called callback didn't have a
**named parameter, so the system would abort (well, log an error) trying
to call it.
Is it a huge overhead? No. But its unnecessary if you've got a tool
that takes care of that "implementation-level" detail for you. Compared
to robustApply, the difference is pretty minimal. If you define a
**named parameter, for instance, you will get the remainder of the
robustApply environment passed in.
In another lifetime, you could imagine these systems being written as
wrappers that added a nested scope around the definition of the function
such that the scope forwarded unresolved name references to the
environment of the event (non-lexical scoping), but that would lack the
explicitness of passing in the arguments from the environment and would,
I suppose be evil in a quite non-trivial way.
Peace, and have fun,
Mike C. Fletcher
Designer, VR Plumber, Coder
More information about the Python-list