在Python 3中使用partial在元类中创建实例方法

发布于 2024-10-19 19:52:24 字数 1215 浏览 7 评论 0原文

使用元类,我尝试通过简化现有实例方法来创建实例方法。问题是部分不适用于实例方法。这是我试图实现的一个简单示例:

from functools import partial

class Aclass(object):

    def __init__(self, value):
        self._value = value

    def complex(self, a, b):                                            
        return a + b + self._value

class Atype(type):

    def __new__(cls, name, bases, attrs):
        return super(Atype, cls).__new__(cls, name, (Aclass, ) + bases, attrs)

    def __init__(cls, name, bases, attrs):
        setattr(cls, 'simple', partial(cls.complex, b=1))

class B(metaclass=Atype):
    pass

b = B(10)

print(b.complex(1, 2))
print(b.simple(1))

输出是:

13
Traceback (most recent call last):
  File "metatest.py", line 22, in <module>
    print(b.simple(1))
TypeError: complex() takes exactly 3 non-keyword positional arguments (1 given)

我已经解决了使用 lambda 将:更改

    setattr(cls, 'simple', partial(cls.complex, b=1))

为:

    setattr(cls, 'simple', lambda self, x: cls.complex(self, x, b=1))

但它很丑陋并且在可选参数方面存在问题。

我可以在实例 __init__ 中创建这些方法,但我想这更有意义,并且使用元类在类 __init__ 上执行此操作更有效。

有什么想法如何正确地做到这一点吗?

Using metaclasses, I am trying to create an instance method by simplifying an existing instance method. The problem is that partial does not work with instance method. This is a simple example of what I try to achieve:

from functools import partial

class Aclass(object):

    def __init__(self, value):
        self._value = value

    def complex(self, a, b):                                            
        return a + b + self._value

class Atype(type):

    def __new__(cls, name, bases, attrs):
        return super(Atype, cls).__new__(cls, name, (Aclass, ) + bases, attrs)

    def __init__(cls, name, bases, attrs):
        setattr(cls, 'simple', partial(cls.complex, b=1))

class B(metaclass=Atype):
    pass

b = B(10)

print(b.complex(1, 2))
print(b.simple(1))

and the output is:

13
Traceback (most recent call last):
  File "metatest.py", line 22, in <module>
    print(b.simple(1))
TypeError: complex() takes exactly 3 non-keyword positional arguments (1 given)

I have solved using lambda changing:

    setattr(cls, 'simple', partial(cls.complex, b=1))

to:

    setattr(cls, 'simple', lambda self, x: cls.complex(self, x, b=1))

but it is ugly and has problems with optional parameters.

I could create these method at the instance __init__ but I guess it makes more sense, and is more efficient to do it on class __init__using metaclasses.

Any ideas how to do it properly?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

最偏执的依靠 2024-10-26 19:52:24

好吧,我对 Python 3 方法处理还有点不熟悉 -
我能想到的最简单的事情是重写 partial ,以便保留原始调用中的第一个参数,然后插入“部分”参数。

它适用于您的示例,但需要使用更复杂的模式进行测试。

from functools import wraps

class Aclass(object):
    def __init__(self, value):
        self._value = value

    def complex(self, a, b):                                            
        return a + b + self._value

def repartial(func, *parameters, **kparms):
    @wraps(func)
    def wrapped(self, *args, **kw):
        kw.update(kparms)
        return func(self, *(args + parameters), **kw)
    return wrapped

class Atype(type):
    def __new__(cls, name, bases, attrs):
        return super(Atype, cls).__new__(cls, name, (Aclass, ) + bases, attrs)

    def __init__(cls, name, bases, attrs):
        setattr(cls, 'simple', repartial(cls.complex, b=1))

class B(metaclass=Atype):
    pass

b = B(10)

print(b.complex(1, 2))
print(b.simple(1))

Well, I am a bit unfamiliar with Python 3 method handling yet -
the simplest thing I could think of is rewriting partial so that it preserves the first argument from the original call, then inserts the "partial" parameters.

It worked with your example, but it needs testing with more complex patterns.

from functools import wraps

class Aclass(object):
    def __init__(self, value):
        self._value = value

    def complex(self, a, b):                                            
        return a + b + self._value

def repartial(func, *parameters, **kparms):
    @wraps(func)
    def wrapped(self, *args, **kw):
        kw.update(kparms)
        return func(self, *(args + parameters), **kw)
    return wrapped

class Atype(type):
    def __new__(cls, name, bases, attrs):
        return super(Atype, cls).__new__(cls, name, (Aclass, ) + bases, attrs)

    def __init__(cls, name, bases, attrs):
        setattr(cls, 'simple', repartial(cls.complex, b=1))

class B(metaclass=Atype):
    pass

b = B(10)

print(b.complex(1, 2))
print(b.simple(1))
就此别过 2024-10-26 19:52:24

我不使用 partial,而是像这样定义类 Atype

class Atype(type):

    def __new__(cls, name, bases, attrs):
        return super(Atype, cls).__new__(cls, name, (Aclass, ) + bases, attrs)

    def __init__(cls, name, bases, attrs):
        def simple(self, a):
            return cls.complex(self, a, 1)
        setattr(cls, 'simple', simple)

__init__() 方法也可以写得更紧凑:

def __init__(cls, name, bases, attrs):
    setattr(cls, 'simple', lambda self, a: cls.complex(self, a, 1))

Instead of using partial, I'd just define class Atype like this:

class Atype(type):

    def __new__(cls, name, bases, attrs):
        return super(Atype, cls).__new__(cls, name, (Aclass, ) + bases, attrs)

    def __init__(cls, name, bases, attrs):
        def simple(self, a):
            return cls.complex(self, a, 1)
        setattr(cls, 'simple', simple)

The __init__() method can also be written more compactly:

def __init__(cls, name, bases, attrs):
    setattr(cls, 'simple', lambda self, a: cls.complex(self, a, 1))
还在原地等你 2024-10-26 19:52:24

问题是 functools.partial() 返回的对象是可调用对象,而不是函数。显然,Python 并不关心非函数试图在这种情况下表现得像函数一样。一种解决方案是创建一个函数作为 partial 对象的包装器。

class Atype(type):

    def __init__(cls, name, bases, attrs):
        simple = partial(cls.complex, b=1)
        setattr(cls, 'simple', lambda cls, a: simple(cls, a))

jsbueno 的解决方案(返回真实函数的 partial 的重新实现)很好。我真的不知道为什么 functools.partial() 不能那样工作;无法在这种情况下使用它是一个令人惊讶的陷阱。

The problem is that the object returned by functools.partial() is a callable object, not a function. So apparently Python doesn't care for a non-function trying to act like one in this context. One solution is to create a function as a wrapper for the partial object.

class Atype(type):

    def __init__(cls, name, bases, attrs):
        simple = partial(cls.complex, b=1)
        setattr(cls, 'simple', lambda cls, a: simple(cls, a))

jsbueno's solution (a reimplementation of partial that returns a real function) is good though. I really don't know why functools.partial() doesn't work that way; not being able to use it in this context is a surprising pitfall.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文