Python 魔法元类创建

3

基本上,我想创建一个“模板”类型的对象,其中包含 staticmethods。然后创建一个继承自该对象的新对象,但使用父对象的方法,使用 functools.partial,其中使用子对象的参数。子对象的参数与父对象方法的参数具有相同的名称。结果范式将类似于以下内容:

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?"

以下是一个不起作用的尝试:

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)

不太熟悉元编程。但是如果__init__中的attrs参数是一个字典,那么你的if callable(attr)似乎是在检查attr键是否可调用。键是字符串,所以它不可调用,对吧?也许if callable(attrs[attr])可以工作? - Håken Lid
谢谢!我修改了代码,现在它可以正常工作了。 - Skorpeo
那就是问题的原因吗?虽然这只是一个猜测,但如果它有效,我可以将其发布为答案而不是评论。 - Håken Lid
不幸的是,它只修复了那一部分...它并没有解决整个问题:/ - Skorpeo
我非常确定使用元类是不可能的:在定义类时(也就是元类执行其操作时),您无法知道在实例创建时所做的映射。 - L3viathan
1个回答

2

我不知道如何使用元类来实现这个,但是下面是一种可能的解决方案,使用一个mixin类。你可能可以像这样使用。

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?"

嗨,我会将此标记为答案,尽管我正在寻找元类解决方案,这可能是不可能的... - Skorpeo
当然。如果有人提交了更加有用的答案,您可以随时将其标记为已接受。如果您想使用元类找出解决方案,我推荐Luciano Ramalho的书《Fluent Python》(http://shop.oreilly.com/product/0636920032519.do),其中包含有关类元编程和类装饰器的章节(这也可能在这里使用)。 - Håken Lid
谢谢,那是最好的书籍之一 : )。我会再看一遍。 - Skorpeo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接