3

Basically, I want to create a "template" type-object with staticmethods and then create a new object that inherits from it but uses the parent's methods using functools.partial where the arguments of the child are used. The child's arguments have the same name as the arguments for the methods of the parent. The resulting paradigm would be something like this:

class Naked:

    @staticmethod        
    def echo1(foo):
        return f"echo1: {foo}"

    @staticmethod        
    def echo2(bar, bla):
        return f"echo2: {bar}, {bla}"

class Clothed(Naked):
    def __init__(self, *args, **kwargs):
        for arg in args:
            setattr(self, arg, arg)

        for k,v in kwargs.items():
            setattr(self, k, v)

a = Clothed(foo = "Hello", bar = "World")
a.echo1 == "echo1: Hello"
a.echo2("wsup?") == "echo2: World, wsup?"

Here is an attempt that doesn't work:

from inspect import signature
from functools import partial
class Meta(type):
    def __init__(cls, name, bases, attrs):
         funcs = (k for k,v in attrs.items() if callable(v) and k not in dir(type)) #this doesn't work...
         for func in funcs:
            args = signature(func).keys() #function argument names
            newargs = {arg:getattr(self, arg, None) for arg in args} #create dictionary with val from instance, how is this possible, no self here?
            attrs[func] = partial(func,newargs)
        return type.__init__(cls, name, bases, attrs)  

class Naked(metaclass=Meta):

    @staticmethod        
    def echo1(foo):
        return f"echo1: {foo}"

    @staticmethod        
    def echo2(bar, bla):
        return f"echo2: {bar}, {bla}"

class Clothed(Naked):
    def __init__(self, *args, **kwargs):
        for arg in args:
            setattr(self, arg, arg)

        for k,v in kwargs.items():
            setattr(self, k, v)
Skorpeo
  • 2,362
  • 2
  • 15
  • 20
  • Not very familiar with metaprogramming. But if the `attrs` argument to `__init__` is a dictionary, then it seems your `if callable(attr)` is checking if the attr key is callable. The key is a string, so it's not callable, right? Perhaps `if callable(attrs[attr])` works? – Håken Lid May 15 '18 at 20:24
  • thanks! I modified it so that piece works now – Skorpeo May 15 '18 at 20:50
  • So that was the cause of the problem? It was a wild guess, but if it worked, I can post it as an answer instead of as a comment. – Håken Lid May 15 '18 at 20:53
  • unfortunately it only fixes that piece... no it doesn't solve the issue at large :/ – Skorpeo May 15 '18 at 20:54
  • I'm pretty sure that's impossible with metaclasses: You don't know that mapping you make (at instance creation time) when the class is defined (which is when the metaclass does its thing). – L3viathan May 16 '18 at 20:22

1 Answers1

2

I don't know how to do this with a metaclass, but here's a possible solution using a mixin class. You might be able to use something like this.

from inspect import signature
from functools import partialmethod, partial

class Naked:

    @staticmethod        
    def echo1(foo):
        return f"echo1: {foo}"

    @staticmethod        
    def echo2(bla, bar):
        return f"echo2: {bar}, {bla}"

class PartialMixin:
    def __init__(self, **kwargs):
        methods = [m for m in dir(self) if callable(getattr(self, m)) and m not in dir(type)]
        for method_name in methods:
            method = getattr(self, method_name)
            method_kwargs = signature(method).parameters.keys()
            partial_kwargs = {k:kwargs[k] for k in kwargs if k in method_kwargs}
            new_method = partial(method, **partial_kwargs)
            # if the methods are not static, maybe functools.partialmethod should be used.
            setattr(self, method_name, new_method)

class Clothed(PartialMixin, Naked):
    pass

a = Clothed(foo = "Hello", bar = "World")
a.echo1() == "echo1: Hello"
a.echo2("wsup?") == "echo2: World, wsup?"
Håken Lid
  • 22,318
  • 9
  • 52
  • 67
  • Hi, i will mark this as the answer albeit I was looking for metaclass solution, which may not be possible... – Skorpeo Jun 01 '18 at 11:50
  • Sure. If someone submits a answer that's more useful, you can always mark that one as accepted instead. If you want to figure out a solution using metaclasses, I recommend the book [Fluent Python](http://shop.oreilly.com/product/0636920032519.do) by Luciano Ramalho, which contains chapters about class metaprogramming and about class decorators (which could also possibly be used here). – Håken Lid Jun 01 '18 at 13:32
  • thanks, that's one of the best books out there :). I will take a look again – Skorpeo Jun 01 '18 at 13:47