Requiring decorators with side-effects

Sometimes, side-effects on decorators are necessary, and we should not delay their execution until the very last possible time, because that's part of the mechanism which is required for them to work.

One common scenario for when we don't want to delay the side-effect of decorators is when we need to register objects to a public registry that will be available in the module.

For instance, going back to our previous event system example, we now want to only make some events available in the module, but not all of them. In the hierarchy of events, we might want to have some intermediate classes that are not actual events we want to process on the system, but some of their derivative classes instead.

Instead of flagging each class based on whether it's going to be processed or not, we could explicitly register each class through a decorator.

In this case, we have a class for all events that relate to the activities of a user. However, this is just an intermediate table for the types of event we actually want, namely UserLoginEvent and UserLogoutEvent:

EVENTS_REGISTRY = {}


def register_event(event_cls):
    """Place the class for the event into the registry to make it 
accessible in the module. """ EVENTS_REGISTRY[event_cls.__name__] = event_cls return event_cls class Event: """A base event object""" class UserEvent: TYPE = "user" @register_event class UserLoginEvent(UserEvent): """Represents the event of a user when it has just accessed the system.""" @register_event class UserLogoutEvent(UserEvent): """Event triggered right after a user abandoned the system."""

When we look at the preceding code, it seems that EVENTS_REGISTRY is empty, but after importing something from this module, it will get populated with all of the classes that are under the register_event decorator:

>>> from decorator_side_effects_2 import EVENTS_REGISTRY
>>> EVENTS_REGISTRY
{'UserLoginEvent': decorator_side_effects_2.UserLoginEvent,
 'UserLogoutEvent': decorator_side_effects_2.UserLogoutEvent}

This might seem like it's hard to read, or even misleading, because EVENTS_REGISTRY will have its final value at runtime, right after the module was imported, and we cannot easily predict its value by just looking at the code.

While that is true, in some cases this pattern is justified. In fact, many web frameworks or well-known libraries use this to work and expose objects or make them available.

It is also true that in this case, the decorator is not changing the wrapped object, nor altering the way it works in any way. However, the important note here is that, if we were to do some modifications and define an internal function that modifies the wrapped object, we would still probably want the code that registers the resulting object outside it.

Notice the use of the word outside. It does not necessarily mean before, it's just not part of the same closure; but it's in the outer scope, so it's not delayed until runtime.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.4.44