Class Decoration

Both techniques, Observable Decoration and Observer Decoration, are static, in the sense, decorations are done e.g. in @-notation evaluated at compile time. They are applied to static functions.

Decoration of a class by default addresses decoration of the class initializer, this means

Pyc. 44 Decoration of a class
@Observable
class Dog:
    def __init__(self):
        pass                    # Some code ...

should be understood as

Pyc. 45 Decoration of the class initializer
class Dog:
    @Observable
    def __init__(self):
        pass                    # Some code ...

But this behavior is insidious, e.g.

Pyc. 46 Decoration for a new instance
from decoratory.observer import Observable

class Person:
    def __init__(self, name: str = "Jane Doe"):
        print(f"{name} arrived.")

@Observable(observers=Person)
class Dog:
    def __init__(self, name: str = "Teddy"):
        print(f"Dog {name} arrived.")

# Case 1: Dog is an observable to Person
prs = Person()                  # Jane Doe arrived.
dog = Dog()                     # Dog Teddy arrived.
                                # Jane Doe arrived.

The instantiation of Dog induced an instantiation of Person.

Warning

Class decoration — Take care when decorating a class initializer

Notifying the __init__ method of an observer results in a new instance! This means calling the observable induces instantiation of a new observer object, surely in not any case this is the desired behavior …

So the decoration should not address a class but one (or more) target methods of the class. As already mentioned, this is easy if this callback function is a @staticmethod or @classmethod.

Pyc. 47 Decoration of a @staticmethod or @classmethod
class Person:
    def __init__(self, name: str = "Jane Doe"):
        print(f"{name} arrived.")

    @staticmethod
    def action1(act: str = "Hello?"):
        print(f"Person says {act}")

    @classmethod
    def action2(cls, act: str = "What's up?"):
        print(f"Person says {act}")

@Observable(observers=[Person.action1, Person.action2])
class Dog:
    def __init__(self, name: str = "Teddy"):
        print(f"Dog {name} arrived.")

# Case 2: Dog is an observable to Person.action
prs = Person()                  # Jane Doe arrived.
dog = Dog()                     # Dog Teddy arrived.
                                # Person says Hello?
                                # Person says What's up?

This is how it usually works: one action of the observable, here it’s the instantiation of Dog, triggers one to many actions at each observer, here the two actions of Person.

But often an instance method is also interesting as a callback function:

  • If a particular instance prs = Person(name="John Doe") of a person is meant, a decoration like @Observable(observers=prs.action) with the instance method can be applied to Dog.

  • For an arbitrary instance of a person @Observable(observers=Person().action) works.

Even a list of F structures would be possible to optionally submit different parameters.

Pyc. 48 Observable decoration with a list of observer instances
from decoratory.observer import Observable
from decoratory.basic import F

class Person:
    def __init__(self, name: str = "Jane Doe"):
        self.name = name
        print(f"{name} arrived.")

    def action(self, act: str = "Hello?"):
        print(f"{self.name} says {act}")

prs1 = Person()                 # Jane Doe arrived.
prs2 = Person("John Doe")       # John Doe arrived.

@Observable(observers=[prs1.action, F(prs2.action, "What's up?")])
class Dog:
    def __init__(self, name: str = "Teddy"):
        print(f"Dog {name} arrived.")

# Case 3: Dog is an observable to actions of various person instances.
dog = Dog()                     # Dog Teddy arrived.
                                # Jane Doe says Hello?
                                # John Doe says What's up?

Here, one action of the observable, the instantiation of Dog, triggers one to many actions at each selected resp. instantiated (but not generally at each!) observer of Person. In such situations, a late dynamic decoration Pyc. 1 could be a good idea.

So far, instantiating Dog resulted in an information and induced action at Person. If Dog has its own actions that need to be selectively monitored, each of the selected actions can of course be decorated individually as an Observable. For the sake of a better overview, this can also be done on the class itself.

Pyc. 49 Observable decoration using method lists
class Person:
    def __init__(self, name: str = "Jane Doe"):
        self.name = name
        print(f"{name} arrived.")

    @classmethod
    def actionA(cls, act: str = "Hello?"):
        print(f"Person says {act}")

    def actionB(self, act: str = "Hello?"):
        print(f"{self.name} says {act}")

@Observable(methods=["action1", "action2"],
            observers=[Person.actionA, Person("Any Doe").actionB])
class Dog:
    def __init__(self, name: str = "Teddy"):
        self.name = name
        print(f"Dog {name} arrived.")

    @staticmethod
    def action1(act: str = "Woof!"):
        print(f"Dog acts {act}")

    def action2(self, act: str = "Brrr!"):
        print(f"{self.name} acts {act}")

# Case 4: Dog is an observable with selected actions.
                                # Any Doe arrived.
prs = Person()                  # Jane Doe arrived.
dog = Dog()                     # Dog Teddy arrived.

dog.action1()                   # Dog acts Woof!       (@staticmethod)
                                # Person says Hello?   (@classmethod)
                                # Any Doe says Hello?  (Instance 'Any')

Dog.action2(dog)                # Teddy acts Brrr!     (Instance 'Teddy')
                                # Person says Hello?   (@classmethod)
                                # Any Doe says Hello?  (Instance 'Any')

The last line Dog.action2(dog) provides the instance of Teddy as the first argument, self. This works because internally the class method Dog.action2 was registered instead of an instance method that didn’t exist at compile time. On the other hand, the call dog.action2() fails because this instance method was not registered. But, if this is what is to be achieved, an instance method can first be created and registered, just as seen above in Class Decoration, Case 3.


◄ prev

up

next ►

Legal Notice

Privacy Policy

Cookie Consent

Sphinx 7.2.6 & Alabaster 0.7.12

© Copyright 2020-, Martin Abel, eVation. All Rights Reserved.