emptypage.jp > Notes
Original: 日本語 (Japanese)
The event mechanism in C# is nice. Some people implement the same mechanism in their favorite languages other than C#. You can find a lot of event implementations in Python too; see search results for “c# event python” in Google for instance. This article shows yet another implementation of event mechanism in Python, which is a little bit improved by using an interesting feature of Python, descriptor.
(TODO: Maybe I should explain the C#’s event mechanism and show how wonderful it is. I, however, don't have much time to do that now.)
Let's start with a simple implementation:
The last 3-line-code enables you to write your code like
e += handler,
e -= handler and
e(earg) instead of
e.fire(earg). See Python document about these special methods.
A script which uses this module will be like this:
When you run this script, you’ll get:
C:\...>python event_sample.py foo!
That’s OK. How about that? If you are sure that they understand what event mechanism is and how to use our module, that’s totally OK. However, We can point some problems out abount this:
Publisherto know what kind of event does
Publisherfire (and even whether it fires some kinds of events), because all these event stuff is prepered in the
selfas the first argument in calling event handlers, like
Better documentation may help you though, let’s think more efficient way.
You may pass the sender objects of the events in
__init__, to fire events like
self.evt_foo(). However, this approach needs codes like
self.evt_foo = eventEvent(self) in
__init__, so only moves points of the problem.
Another approach: you design a class (e.g. that is called
EventPublisher) with a mechanism that fires event, and ask users to subclass it. However, it is not recommended that forcing subclassing upon them. We prefer casual way.
Imagine defining events of a class as just like a methods or properties of that. Is it impossible unless revising the specifications of Python? Well, no, it is possible; think how they implement the Python's
Here’s the implementation that avoids the matters above.
Before event.py, Let’s look at the sample script code which uses it. You will find that the event-related codes are written more declaratively and naturally there:
Look at the
Publisher class. Now events are members of the class (not of instance). You can easily know what kind of events this class publishes with this way. And you can type less. Aregument of
event.Event contains document about the event itself. They are used when user do
Publisher.evt_foo event should be published when
foo method runs, and the code corresponds to this is
self.evt_foo(). Calling class member object like a method: event handlers are still called with the
Publisher instance as the first argument (Trust me). We should write publisher object down explicitly in the simpler implementation but we are free from that now.
self.evt_foo() may called with an argument such as
self.evt_foo(earg). Event handler function shoud accept two arguments: the publisher object (
sender) and the event-defined custom value (
None will be passed to
earg when you fires event without argument like
I think the improved version looks more suitable for our purpose, doesn’t it?
Result (same as that of the simpler one):
C:\...>python event_sample.py foo!
Here is the code of our improved event.py. More lines but still less than 100.
You may find some unfamiliar special method names.
The event objects in the sample script are called like methods, without caller object itself as an argument. How that can be done? Let’s launch Python interpreter and watch behaviour of event.py for a while:
C:\...>python Python 2.6.5 (r265:79096, Mar 19 2010, 21:48:26) [MSC v.1500 32 bit (Intel)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> import event >>> class C(object): ... evt = event.Event('test') ... >>> C.evt <event.Event object at 0x00AF4A70> >>> c = C() >>> c <__main__.C object at 0x00AF4A30> >>> c.evt <event.EventHandler object at 0x00AF4950> >>>
You see class member
C.evt and instance member
c.evt are not the same object (see the IDs of each object are different). We got an
event.EventHandler instance in the later case. The role of this object is to bind the instance object and the event object of the class. Every
event.EventHandler instance has informations about the instances and the event object. You can see that by doing like this:
>>> c.evt.obj <__main__.C object at 0x00AF4A30> >>> c.evt.event <event.Event object at 0x00AF4A70> >>>
This is achived by a feature of Python language called descriptor. For instance, you instantiate
Foo class and set it as a member of
x.foo = Foo(). When the
Foo instance object is evaluated, is there any way for
x.foo to know
x, the object which contains itself? Descriptor is the way. In other words, it is the mechanism that enables object to know its context on the code. In the output above,
C.evt detects whether its owner is a class object (
C) or a instance object (
c) and returns different result depending on it.
Event class has a method called
__get__. It is a special method for descriptor. When
C.evt is evaluated, this method is called as
self.__get__(None, C). And when
c.evt is evaluated, this is called like
self.__get__(c, C). On our code, we return
event.Event instance itself when the first argument of the method is
None, while return
EventHandler(self, obj) instance when it is a class instance object.
Standard functions such as
staticmethod are also implemented with using descriptor (they are implemented in C but its inside is the same).
See references below about the details of the descriptor.
As to implementing event mechanism in Python, it is hard with simple approaches to sweep redundancy of the code such as
self.evt = event.Event(self). They will require two “
self”s for the caller and the argument or the right value and the left value in the same line. Descriptor prvides the solution. The event implementation with using descriptor is easy to use and free from any redundant coding restriction. I think implementing event mechanism is one of the most suitable cases for the descriptor feature.
Descriptor is not an all-round player but it can be as effective as the specification of the Python language got extended depending on your idea.
I thank all the developers and writers who wrote about implementations of event mechanism, descriptor and any other articles and reference codes.
Though I considered the correctness of this article, there may still remain errors or mistakes. I do not guarantee the correctness of the content. Comments are always welcomed. You can keep in touch with me in my guestbook page or e-mail below.
This article is licensed under the Creative Commons CC BY License. All the codes on the article are in the public domain.
Copyright 2010-2011 Masaaki Shibata <mshibata at emptypage.jp>