1

Is it possible to create a "constructor".. or rather "Initializer" to each function, instead of having to manually write it at the top of each function in class?

So, each time a function in a class is called, the other assigned function (unknown to caller) is always called first (called pre_check in below example).

An example using super(), but I then have to manually copy it inside each function.

class Helper():
    def pre_check(self):
        print("Helper fcn")


class Parent(Helper):
    def __init__(self):
        print("Initializer")

    def foo(self):
        super().pre_check()  # <---- new code
        # ... existing code here ...

    def bar(self):
        super().pre_check()  # <---- new code
        # ... existing code here ...

    def many_more_functions(self):
        super().pre_check()  # <---- new code
        # ... existing code here ...

m = Parent()

m.foo()
m.bar()

Note how __init__ in Parent is not supposed to run pre_check.

10
  • 3
    Perhaps you're looking for decorators, but you will still have to decorate the methods. Maybe there are class decorators Commented Jun 13, 2019 at 11:10
  • Your code seems to work fine. What you are looking for please clarify? Commented Jun 13, 2019 at 11:10
  • Possible duplicate of How to decorate a class? Commented Jun 13, 2019 at 11:11
  • 3
    @AmazingThingsAroundYou I think OP would just prefer to not have to copy paste super().pre_check() part to each method. Commented Jun 13, 2019 at 11:12
  • 1
    unless Parent is a Helper, it should probably not inherit from Helper. Commented Jun 13, 2019 at 11:17

3 Answers 3

2

You can use a decorator for the class that will in turn decorate all public methods defined in the class:

def addhelper(helpmethod):
    def deco(cls):
        def decomethod(method):
            def inner(self, *args, **kwargs):
                helpmethod(self)
                return method(self, *args, **kwargs)
            # copy signature, doc and names from the original method
            inner.__signature__ = inspect.signature(method)
            inner.__doc__ = method.__doc__
            inner.__name__ = method.__name__
            inner.__qualname__ = method.__qualname__
            return inner
        # search all methods declared in cls with a name not starting with _
        for name, meth in inspect.getmembers(
            cls,lambda x: inspect.isfunction(x)
            and not x.__name__.startswith('_')
            and x.__qualname__.startswith(cls.__name__)):
            # replace each method with its decoration
            setattr(cls, name, decomethod(meth))
        return cls
    return deco

class Helper():
    def pre_check(self):
        print("Helper fcn")

@addhelper(Helper.pre_check)
class Parent(Helper):
    def __init__(self):
        print("Initializer")

    def foo(self):
#        super().pre_check()  # <----
        print('in foo')

    def bar(self):
#        super().pre_check()  # <----
        print('in bar')

    def many_more_functions(self):
#        super().pre_check()  # <----
        print('in many_more_functions')

We can now use it:

>>> p = Parent()
Initializer
>>> p.foo()
Helper fcn
in foo
>>> p.bar()
Helper fcn
in bar
>>> p.many_more_functions()
Helper fcn
in many_more_functions
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, played a bit with it and it seems do the job
1

Use __init_subclass__ to change subclasses as they are created. You can wrap the methods of subclasses:

class Helper():
    def __init_subclass__(cls):
        for field, value in cls.__dict__.items():
            # add additional checks as desired, e.g. exclude __special_methods__
            if inspect.isfunction(value) and not getattr(value, 'checked', False):
                setattr(cls, field, cls._check(value))  # wrap method

    @classmethod
    def _check(cls, fcn):
        """Create a wrapper to inspect the arguments passed to methods"""
        @functools.wraps(fcn)
        def checked_fcn(*args, **kwargs):
            print(fcn, "got", args, kwargs)
            return fcn(*args, **kwargs)
        return checked_fcn


class Parent(Helper):
    def __init__(self):
        print("Initializer")

    def foo(self):
        print("Foo")

Note that this will wrap all methods, including special methods such as __init__:

>>> Parent().foo()
<function Parent.__init__ at 0x1029b2378> got (<__main__.Parent object at 0x102c09080>,) {}
Initializer
<function Parent.foo at 0x1029b2158> got (<__main__.Parent object at 0x102c09080>,) {}
Foo

You can extend the check in __init_subclass__ with arbitrary rules to filter out functions. For example, field[:2] == field[-2:] == "__" excludes special methods.

5 Comments

Good edit! As said to @Serge Ballesta, thanks, played a bit with it and it seems do the job
Accepted this answer as oppsosed to @Serge Ballesta, which also works flawlessly, since it is less "complex" and I can understand what is going on. But maybe his is more robust.. I dont know.
do you know why this is not working on python 3.5.3? It works on python 3.6.7
The hook was added in Python 3.6. docs.python.org/3/reference/…
Aha... thanks! Then I guess it's time to upgrade ;)
1

You can use metaclass and define a decorator for each method in the instance of that metaclass

Code :

def decorate(f):
    def do_something(self, a):

        if (f(self, a) > 18) :
            return ("Eligible to vote")
        else :
            return ("Not eligible to vote")

    return do_something


class Meta(type):
    def __new__(cls, name, bases, namespace, **kwds):
        namespace = {k: v if k.startswith('__') else decorate(v) for k, v in namespace.items()}
        return type.__new__(cls, name, bases, namespace)


class MetaInstance(metaclass=Meta):

    def foo1(self, val):
        return val + 15

    def foo2(self, val):
        return val + 9

obj1 = MetaInstance()
print(obj1.foo1(5))
print(obj1.foo2(2))

2 Comments

Thanks, I've edited the question. The way I see your proposed answer decorates / replaces the entire function in foo1 and foo2, nothing added inside them will execute !?. I like the filtering of methods starting with "__".
the decorator does not replace the function in foo1 and foo2, foo1 and foo2 accept the respective values and then returns the computed ones to the decorator which then looks at if the returned value (from the respective function) is greater than 18 or not and prints eligible or not eligible to vote

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.