【问题标题】:Create instance method in metaclass using partial in Python 3在 Python 3 中使用 partial 在元类中创建实例方法
【发布时间】:2011-03-01 22:18:18
【问题描述】:

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

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__ 类上使用元类更有效。

任何想法如何正确地做到这一点?

【问题讨论】:

  • @martineau:我完全赞成使用 python-3.x 标签,但我认为没有任何理由不使用“python”。另外 - “python3”不是更干净的标签吗?
  • @jsbueno:同意——但它不允许我将 python-3.x 更改为 python3。
  • @jsbueno:标签是python-3.x,我不知道(如果有的话)这些事情是如何决定的。我怀疑它可以在元上讨论,然后如果有支持就改变。无论如何python3是一个同义词,所以你可以输入它,它会被重新映射。

标签: python methods python-3.x partial


【解决方案1】:

好吧,我对 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))

【讨论】:

  • 我认为wrapper() 会是一个更好的——更准确更准确的——你在repartial() 中调用wrapped() 的嵌套函数的名称。只是一个挑剔的好答案。
【解决方案2】:

我不使用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))

【讨论】:

  • 是的,我真的不认为这里需要元类(或functools.partial())。也许这只是一个简单的例子来说明这个问题,然而,在 user482819 的实际应用程序中确实需要元类。
  • 确实,。我需要使用元类根据类的特定属性动态创建实例方法。
【解决方案3】:

问题是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() 不能那样工作;无法在这种情况下使用它是一个令人惊讶的陷阱。

【讨论】:

  • 这是解决partial 陷阱的好方法,但我认为我自己的答案更好;-) 因为,一方面,它具有较少的函数调用开销。顺便说一句,到目前为止没有人提到的问题的另一个原因是 cls.complex 是一个未绑定的方法。
  • 在 Python 3 中似乎没有未绑定的方法; Aclass.complex 是一个函数。
  • 有趣的是,当部分作为属性的 fset 或 fget 给出时,它可以正常工作。
  • 我刚刚从 Guido 找到这篇文章,它与这篇文章非常相关:python-history.blogspot.com/2010/06/…
  • 所以functools.partial() 需要返回一个带有__get()__ 方法的对象,该方法做正确的事情......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-03-01
  • 2017-08-07
  • 2013-08-12
  • 1970-01-01
  • 1970-01-01
  • 2021-08-02
  • 1970-01-01
相关资源
最近更新 更多