Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Move dictionary from instance to class level

1 view
Skip to first unread message

Frank Millman

unread,
Aug 26, 2009, 4:22:41 AM8/26/09
to
Hi all

I have a class that uses a dictionary to map message numbers to methods.

Here is a simple example -

class MyClass(object):
def __init__(self):
self.method_dict = {}
self.method_dict[0] = self.method_0
self.method_dict[1] = self.method_1

def on_message_received(self, msg):
self.method_dict[msg]()

def method_0(self):
print 'in method_0'

def method_1(self):
print 'in method_1'

I have quite a few methods, so the dictionary is growing - up to 28 methods
so far. To avoid
having to recreate the dictionary every time I create an instance of the
class, I tried to move it
up to the class level. Unfortunately it does not work. This is what I
tried -

class MyClass(object):
method_dict = {}
method_dict[0] = method_0 # this gives an error
method_dict[1] = method_1

def on_message_received(self, msg):
self.method_dict[msg]()

def method_0(self):
print 'in method_0'

def method_1(self):
print 'in method_1'

As written above, I get the following error -
NameError: name 'method_0' is not defined

If I try self.method_0, I get 'self' is not defined.

If I try __class__.method_0, I get '__class__' is not defined.

If I try MyClass.method_0, I get 'MyClass' is not defined.

Is there any variation on this theme that will work?

#----------------------------------------------------

Ok, I found a variation that seems to work.

Is this the preferred way, or is there a better alternative?

class MyClass(object):

def on_message_received(self, msg):
#self.method_dict[msg]() # had to change this to get it to work
self.method_dict[msg](self)

def method_0(self):
print 'in method_0'

def method_1(self):
print 'in method_1'

MyClass.method_dict = {}
MyClass.method_dict[0] = MyClass.method_0
MyClass.method_dict[1] = MyClass.method_1

As you can see, I had to add 'self' to the method arguments when calling the
method.

Any comments?

Thanks

Frank Millman


Chris Rebert

unread,
Aug 26, 2009, 4:54:05 AM8/26/09
to Frank Millman, pytho...@python.org

Right, because you're in the *class* body; there's no "current
instance" to be "self"; in fact, there's not even any class for there
to be instances of yet.

> If I try __class__.method_0, I get '__class__' is not defined.

Right, because the class isn't created until its body has finished executing

> If I try MyClass.method_0, I get 'MyClass' is not defined.

See previous note. The class name can't be bound to anything until the
class itself has been created.

> Is there any variation on this theme that will work?
>
> #----------------------------------------------------
>
> Ok, I found a variation that seems to work.
>
> Is this the preferred way, or is there a better alternative?

A

class MyClass(object):
def on_message_received(self, msg):

self.method_dict[msg](self)

def method_0(self):
print 'in method_0'

def method_1(self):
print 'in method_1'

method_dict

method_dict = {0: method_0, 1: method_1}
#note this comes *after* the methods in question have been defined

Is there some reason you aren't using a list instead of a dict?
e.g. method_dict = [method_0, method_1, method_2, etc]

For that matter, why are you associating methods with integers in the
first place?

Cheers,
Chris
--
http://blog.rebertia.com

Frank Millman

unread,
Aug 26, 2009, 5:21:03 AM8/26/09
to

Thanks, Chris. This is much better.

> Is there some reason you aren't using a list instead of a dict?
> e.g. method_dict = [method_0, method_1, method_2, etc]
>
> For that matter, why are you associating methods with integers in the
> first place?
>

I actually use constants to define my messages, like this -

(MESSAGE_ONE
,MESSAGE_TWO
,MESSAGE_THREE
) = xrange(3)

I can then refer to the messages by their description, but internally it
uses integers.

Frank


Frank Millman

unread,
Aug 26, 2009, 5:22:21 AM8/26/09
to
On Aug 26, 10:54 am, Chris Rebert <c...@rebertia.com> wrote:

Thanks, Chris. This is much better.

> Is there some reason you aren't using a list instead of a dict?


> e.g. method_dict = [method_0, method_1, method_2, etc]
>
> For that matter, why are you associating methods with integers in the
> first place?
>

I actually use constants to define my messages, like this -

Frank Millman

unread,
Aug 26, 2009, 5:22:21 AM8/26/09
to
On Aug 26, 10:54 am, Chris Rebert <c...@rebertia.com> wrote:

Thanks, Chris. This is much better.

> Is there some reason you aren't using a list instead of a dict?


> e.g. method_dict = [method_0, method_1, method_2, etc]
>
> For that matter, why are you associating methods with integers in the
> first place?
>

I actually use constants to define my messages, like this -

Frank Millman

unread,
Aug 26, 2009, 5:48:38 AM8/26/09
to

"Frank Millman" <fr...@chagford.com> wrote:

Apologies for the triple-post.

I use google-groups for reading c.l.py, but I know that some people reject
messages from there due to the volume of spam, so on the odd occasion when I
want to send something I fire up Outlook Express and send it from there. It
seems to be misbehaving today.

Sorry about that.

Frank


MRAB

unread,
Aug 26, 2009, 8:40:56 AM8/26/09
to pytho...@python.org
An alternative is:

>>> class MyClass(object):
... def on_message_received(self, msg):
... try:
... getattr(self, "method_%d" % msg)()
... except AttributeError:
... raise Exception("Unknown message")
... def method_0(self):
... print 'in method_0'
... def method_1(self):
... print 'in method_1'
...
>>> my_obj = MyClass()
>>> my_obj.on_message_received(0)
in method_0
>>> my_obj.on_message_received(1)
in method_1
>>> my_obj.on_message_received(2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in on_message_received
Exception: Unknown message
>>>

Frank Millman

unread,
Aug 26, 2009, 10:54:32 AM8/26/09
to

"MRAB" <pyt...@mrabarnett.plus.com> wrote in message
news:mailman.444.1251290...@python.org...

> An alternative is:
>
> >>> class MyClass(object):
> ... def on_message_received(self, msg):
> ... try:
> ... getattr(self, "method_%d" % msg)()
> ... except AttributeError:
> ... raise Exception("Unknown message")
> ... def method_0(self):
> ... print 'in method_0'
> ... def method_1(self):
> ... print 'in method_1'
> ...
> >>> my_obj = MyClass()
> >>> my_obj.on_message_received(0)
> in method_0
> >>> my_obj.on_message_received(1)
> in method_1
> >>> my_obj.on_message_received(2)
> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> File "<stdin>", line 6, in on_message_received
> Exception: Unknown message

I like the idea. Unfortunately my real-world situation is a little more
complex than my simple example implied.

This is a more typical example -

(EVT_GETLOGIN
,EVT_LOGOUT
,EVT_GETMENUOPT
,EVT_GOTFOCUS
,EVT_LOSTFOCUS
,EVT_BUTTONCLICKED
,EVT_CHECKBOX
...
) = xrange(28)

method_dict = {}
method_dict[EVT_GETLOGIN] = onGetLogin
method_dict[EVT_LOGOUT] = onLogout
method_dict[EVT_GETMENUOPT] = onGetMenuOpt
method_dict[EVT_GOTFOCUS] = onGotFocus
method_dict[EVT_LOSTFOCUS] = onLostFocus
method_dict[EVT_BUTTONCLICKED] = onButtonClicked
method_dict[EVT_CHECKBOX] = onCheckBox

You can probably figure out from my method names what I am doing here - the
messages are sent by a thin client (wxPython), and processed by a server.I
am anticipating some "don't do that" responses, but I like the concept, and
so far it is very fast and responsive, so I will pursue it for now.

Thanks for the input.

Frank


Dave Angel

unread,
Aug 26, 2009, 11:54:13 AM8/26/09
to Frank Millman, pytho...@python.org
Any time I see multiple lists like that which have to stay in synch, I
think code-smell.

Why not let the EVT's be passed as strings, and avoid the whole mapping
to integers and mapping back detail? And name the methods involved in a
way that you can directly translate the string to the method name?

Barring that, make a single "structure" which lists the event names and
method names in matched pairs, and derive whatever lists and
dictionaries you actually need from that one structure. And that
structure should be shared between client and server code, perhaps as a
text file that they both parse. Or as a stream that's passed from one
to the other during startup. That way, consistency between them can be
regulated (with version string in the file, for example)

DaveA

Frank Millman

unread,
Aug 27, 2009, 3:10:02 AM8/27/09
to Dave Angel, pytho...@python.org
Dave Angel wrote:

> Any time I see multiple lists like that which have to stay in
> synch, I think code-smell.
>

I don't think it is that bad, but I agree there is always room for
improvement.

> Why not let the EVT's be passed as strings, and avoid the whole mapping
> to integers and mapping back detail? And name the methods involved in a
> way that you can directly translate the string to the method name?
>

There is some merit in this. At present I am writing the server and the
client, and copy/paste the list of constants, so they are guaranteed to stay
in sync. (Maybe I should import it instead - even more fool-proof [the fool
being me, of course]).

However, it may happen that I want to open it up to 3rd-party clients in the
future, in which case I would have to publish some sort of API. It would
make more sense to identify the messages by a meaningful name rather than an
integer.

And yes, I could then use 'getattr' to retrieve the corresponding method on
the server side.

> Barring that, make a single "structure" which lists the event
> names and
> method names in matched pairs, and derive whatever lists and
> dictionaries you actually need from that one structure. And that
> structure should be shared between client and server code,
> perhaps as a
> text file that they both parse. Or as a stream that's passed
> from one
> to the other during startup. That way, consistency between
> them can be
> regulated (with version string in the file, for example)
>

I'm not sure how that would work. On the client side, I need to respond to a
'button-clicked' event from the gui system by sending an EVT_BUTTON_CLICKED
message to the server. In other words it is hard-coded into the client
program. I don't know how that would work if I read in a list of
message-types at startup.

> DaveA
>

Thanks for the input - it is always good to have some fresh ideas.

Frank

Dave Angel

unread,
Aug 27, 2009, 4:36:34 AM8/27/09
to Frank Millman, pytho...@python.org
Frank Millman wrote:
> Dave Angel wrote:
>
>
>> Any time I see multiple lists like that which have to stay in
>> synch, I think code-smell.
>>
>>
>
> I don't think it is that bad, but I agree there is always room for
> improvement.
>
>
>> Why not let the EVT's be passed as strings, and avoid the whole mapping
>> to integers and mapping back detail? And name the methods involved in a
>> way that you can directly translate the string to the method name?
>>
>>
>
> There is some merit in this. At present I am writing the server and the
> client, and copy/paste the list of constants, so they are guaranteed to stay
> in sync. (Maybe I should import it instead - even more fool-proof [the fool
> being me, of course]).
>
> However, it may happen that I want to open it up to 3rd-party clients in the
> future, in which case I would have to publish some sort of API. It would
> make more sense to identify the messages by a meaningful name rather than an
> integer.
>
> And yes, I could then use 'getattr' to retrieve the corresponding method on
> the server side.
>
>
I've built such pairs of programs (not in Python), and studied the
tradeoffs of various methods of coupling. Cut & Paste is probably the
worst way to do it. Manual error is easy to creep in, where one copy is
updated, and somebody neglects to update the other. It's especially bad
if the item order matters, as it does for enums and for your xlist()
example.

Shared code works okay (have a module that both ends import). But it
assumes both ends are built and ship and install on a consistent basis.
Not too hard when it's a single product on a single machine. But if the
two ends are on separate machines, especially separated by the Internet,
you have to deal with version consistency problems. If there's a time
shift as well (file format), then you're stuck at that level. Check the
version, and either insist on exact match, or have a way to adapt to
whichever end reports the earlier version. Make no mistake, there will
be new versions, and you'll have to deal with it.

Shared data is about the same, but can sometimes be more flexibly
interpreted. It also can have the advantage of being able to have
multiple simultaneous versions on the same machine. For the Internet
case, picture a server that has to be able to serve several clients,
each running different versions. And it's tougher for corruption
(accidental or malicious) to do serious damage. I wouldn't want to do
an import over the Internet live connection.

Best is if the necessary data is passed between the two ends during
initialization, and both ends know how to make that data describe the
protocol they'll use. That can work as long as there's a notion of a
session, and preferably if the session is two-way. In the case of file
formats, it means you have this data in the header of the file.

Sometimes for bandwidth reasons, you just want to transmit a version
string, and not the entire table implied by that string. Just realize
the increased risks involved.

Probably the easiest way to stay in synch is if each message is
self-describing. To me that screams "text" for the id's. It makes the
message a bit larger, and you have to decide if it's worth it. The
overhead could very well be lost in the noise of packeting, in any
tcp/ip protocol.

Note that all these have very gray boundaries. And that underneath it
all, it's all just data.


>> Barring that, make a single "structure" which lists the event
>> names and
>> method names in matched pairs, and derive whatever lists and
>> dictionaries you actually need from that one structure. And that
>> structure should be shared between client and server code,
>> perhaps as a
>> text file that they both parse. Or as a stream that's passed
>> from one
>> to the other during startup. That way, consistency between
>> them can be
>> regulated (with version string in the file, for example)
>>
>>
>
> I'm not sure how that would work. On the client side, I need to respond to a
> 'button-clicked' event from the gui system by sending an EVT_BUTTON_CLICKED
> message to the server. In other words it is hard-coded into the client
> program. I don't know how that would work if I read in a list of
> message-types at startup.
>
>
>

Show me a sample client event handler, and maybe I can suggest how to
encode it. For example in wxPython, events are encoded with an event
object. You could have the event send the object's type-string as an
event ID. No lookup at all. And in fact, one event handler then might
handle several of the events for a given widget, or even for multiple
ones. I guess it's not that simple, since you frequently have to pass
other information, such as the state of the Ctrl-key, or the mouse
position on the screen, which is not directly encoded in the event type.

Frank Millman

unread,
Aug 27, 2009, 4:56:47 AM8/27/09
to Dave Angel, pytho...@python.org
Dave Angel wrote:
>
> Show me a sample client event handler, and maybe I can suggest how to
> encode it. For example in wxPython, events are encoded with
> an event
> object. You could have the event send the object's type-string as an
> event ID. No lookup at all. And in fact, one event handler
> then might
> handle several of the events for a given widget, or even for multiple
> ones. I guess it's not that simple, since you frequently
> have to pass
> other information, such as the state of the Ctrl-key, or the mouse
> position on the screen, which is not directly encoded in the
> event type.

That is definitely *not* what I want to do.

I want to make the server as generic as possible, so that it can handle any
type of client, hopefully even including a browser eventually. Therefore the
server has no knowledge of wxPython event types.

I have abstracted all the event types I am interested in (the list is fairly
stable now). My protocol requires that the client maps a specific gui event
type to a message identifier, and the server maps the message identifier to
a method that handles the message.

Hope that makes sense.

Frank

Dave Angel

unread,
Aug 27, 2009, 5:24:14 AM8/27/09
to Frank Millman, pytho...@python.org
Yes, it makes sense. Sorry for heading down that particular dead-end.
But the more I think about it, the more likely I think it is that you'll
be adding new message types, even if they're just variants of ones
already defined. So it might behoove you to have a shared "data
structure" that describes the whole message, not just equates a name to
an integer.

Or consider pickling. I don't know the tradeoffs, but the idea is that
an object is constructed at the other end that has all the same data
members as the object you had at this end. Perhaps with a flexible
enough class definition, you could represent all or at least several of
your messages with the same object.

I am curious about your topology. You're sending events from the client
(which presumably has a mouse and keyboard) to a server. But what does
the server do with those? Does it have its own screen, or what?


DaveA

Frank Millman

unread,
Aug 27, 2009, 6:21:10 AM8/27/09
to Dave Angel, pytho...@python.org
Dave Angel wrote:

I'll try to explain.

I am writing a fairly standard business/accounting application. (I am now
adding Business Process Management to it, which is complicating matters, but
that is another story.)

Most of the gui stuff is basic forms processing - screens with static data,
text fields for display and input of data, and buttons for signifying
certain actions to be taken.

My original attempt had both the gui and the business logic running on the
client, with a connection to a database running on a server. It worked, but
then I realised it was very insecure - it would be easy to hack the python
code, bypass the business logic, and allow any update to the database.

So my second attempt took all the business logic out of the client and put
it onto the server. To execute a form, the server builds a form definition
by creating objects to represent each of the widgets, creates a list of the
components with sufficient information for the client to render them , then
sends a json'd copy of the list to the client, which interprets it and
displays the required form. The client informs the server of each event, and
the server handles the event in much the same way as it did before when the
business logic was on the client.

For example, clicking a button triggers a response which could involve
updating the database, displaying another window with additional data, etc,
etc. Previously there would have been a method on the client designed to
handle the response, and the method would be added to the button constructor
so that it would be called when the button was clicked.

Now the method is on the server, and is stored as an attribute of the button
object on the server. When the user clicks the button, the message is passed
up to the server (each widget has its own id, which is part of the message),
the server is notified that the button was clicked, and it calls the
associated method.

There is a lot more to it than that, but hopefully that will give you an
idea.

If I ever get this to a workable state (it is taking far longer than I
anticipated) I will release it as open source, and will then welcome as much
feedback as possible.

Frank

Anthony Tolle

unread,
Aug 27, 2009, 9:36:25 AM8/27/09
to
To take things one step further, I would recommend using decorators to
allow symbolic association of functions with the message identifiers,
as follows:

======================================

(MESSAGE_ONE
,MESSAGE_TWO
,MESSAGE_THREE
) = xrange(3)

class MyClass(object):
method_dict = {}

# create helper decorator
register_method = lambda msg, method_dict=method_dict: lambda
function: method_dict.setdefault(msg, function)

@register_method(MESSAGE_ONE)
def handle_one(self):
print 'handling MESSAGE_ONE'

@register_method(MESSAGE_TWO)
def handle_two(self):
print 'handling MESSAGE_TWO'

@register_method(MESSAGE_THREE)
def handle_three(self):
print 'handling MESSAGE_THREE'

# no longer need helper decorator
del register_method

# function to dispatch messages
def on_message_received(self, msg):
MyClass.method_dict[msg](self)

x = MyClass()

x.on_message_received(MESSAGE_ONE)
x.on_message_received(MESSAGE_TWO)
x.on_message_received(MESSAGE_THREE)

======================================

Note: the line containing the lambda definition is all one line.

Dave Angel

unread,
Aug 27, 2009, 11:43:22 AM8/27/09
to Frank Millman, pytho...@python.org
OK, that makes good sense. And I withdraw any suggestion to use
pickling, since that could be subject to hacking.

It now appears that the messages are only incidentally GUI events. And
that you would be well advised to make every possible event a separate
message, so that if the server state and the client state get out of
synch, you can readily check and reject such operations. For example,
you'd have a message for pressing the SUBMIT button on a particular
form, but you'd have different message types for other buttons on the
same form, or for SUBMIT on other forms.

DaveA

Frank Millman

unread,
Aug 28, 2009, 2:01:01 AM8/28/09
to

Anthony Tolle wrote:
> To take things one step further, I would recommend using decorators to
> allow symbolic association of functions with the message identifiers,
> as follows:
>

[...]

That's neat. Thanks.

Frank


Frank Millman

unread,
Aug 28, 2009, 2:06:14 AM8/28/09
to
Dave Angel wrote:


> OK, that makes good sense. And I withdraw any suggestion to use pickling,
> since that could be subject to hacking.
>
> It now appears that the messages are only incidentally GUI events. And
> that you would be well advised to make every possible event a separate
> message, so that if the server state and the client state get out of
> synch, you can readily check and reject such operations. For example,
> you'd have a message for pressing the SUBMIT button on a particular form,
> but you'd have different message types for other buttons on the same form,
> or for SUBMIT on other forms.
>

I use the same message type for all SUBMITs, but the body of the message
contains a unique id for each button.

When the server constructs the form, it assigns a unique id to each widget,
and includes this id in the form definition it sends to the client.
Therefore the server and client can unambiguously reference specific
widgets, and there is no real danger of getting out of sync.

Frank


0 new messages