【问题标题】:functools.partial on class methodfunctools.partial 关于类方法
【发布时间】:2013-05-13 16:03:52
【问题描述】:

我正在尝试使用另一个更通用的类方法来定义一些类方法,如下所示:

class RGB(object):
    def __init__(self, red, blue, green):
        super(RGB, self).__init__()
        self._red = red
        self._blue = blue
        self._green = green

    def _color(self, type):
        return getattr(self, type)

    red = functools.partial(_color, type='_red')
    blue = functools.partial(_color, type='_blue')
    green = functools.partial(_color, type='_green')

但是当我尝试调用任何这些方法时,我得到:

rgb = RGB(100, 192, 240)
print rgb.red()
TypeError: _color() takes exactly 2 arguments (1 given)

我猜 self 没有传递给 _color,因为 rgb.red(rgb) 有效。

【问题讨论】:

  • 这是一个有用的问题,我不想偏离重点,但作为您代码的一个附带问题,我想知道您为什么要编写 super(RGB, self)。 __init__(),什么时候RGB继承自object?
  • @CaptainLepton 这是一个很好的做法,以防有一天有人添加了一个超类。
  • @michaelb958--GoFundMonica 你的意思是把RGB 超类变成一个新的书面子类?

标签: python exception methods functools


【解决方案1】:

您正在函数上创建部分,而不是方法。 functools.partial() 对象不是描述符,它们本身不会添加 self 参数并且本身不能充当方法。您可以包装绑定的方法或函数,它们根本不适用于未绑定的方法。这是documented

partial 对象类似于function 对象,因为它们是可调用的、弱可引用的,并且可以具有属性。有一些重要的区别。例如,__name____doc__ 属性不会自动创建。此外,在类中定义的partial 对象的行为类似于静态方法,并且在实例属性查找期间不会转换为绑定方法。

改用propertys;这些描述符:

class RGB(object):
    def __init__(self, red, blue, green):
        super(RGB, self).__init__()
        self._red = red
        self._blue = blue
        self._green = green

    def _color(self, type):
        return getattr(self, type)

    @property
    def red(self): return self._color('_red')
    @property
    def blue(self): return self._color('_blue')
    @property
    def green(self): return self._color('_green')

从 Python 3.4 开始,您可以在此处使用新的 functools.partialmethod() object;绑定到实例时它会做正确的事情:

class RGB(object):
    def __init__(self, red, blue, green):
        super(RGB, self).__init__()
        self._red = red
        self._blue = blue
        self._green = green

    def _color(self, type):
        return getattr(self, type)

    red = functools.partialmethod(_color, type='_red')
    blue = functools.partialmethod(_color, type='_blue')
    green = functools.partialmethod(_color, type='_green')

但必须调用这些,而 property 对象可以用作简单属性。

【讨论】:

  • __init__ 中的 self.red = functools.partial(RGB._color, self, 'red') 怎么样?它也兼容 Python2。
  • @dashesy:当然,但这会将这些对象放在每个实例上(内存成本),也使得子类更难替换它们。
【解决方案2】:

partialmethod 的问题在于它与inspect.signaturefunctools.wraps、...不兼容。

奇怪的是,如果您使用 partial documentation implementation example 自己重新实现 functools.partial,它将起作用:

# Implementation from:
# https://docs.python.org/3/library/functools.html#functools.partial
def partial(func, /, *args, **keywords):
    def newfunc(*fargs, **fkeywords):
        newkeywords = {**keywords, **fkeywords}
        return func(*args, *fargs, **newkeywords)
    newfunc.func = func
    newfunc.args = args
    newfunc.keywords = keywords
    return newfunc
class RGB(object):
    def __init__(self, red, blue, green):
        super(RGB, self).__init__()
        self._red = red
        self._blue = blue
        self._green = green

    def _color(self, type):
        return getattr(self, type)

    red = partial(_color, type='_red')
    blue = partial(_color, type='_blue')
    green = partial(_color, type='_green')

rgb = RGB(100, 192, 240)
print(rgb.red())  # Print red

原因是newfunc 是一个真正的函数,它实现了descriptor protocolnewfunc.__get__。而type(functools.partial) 是一个自定义类,__call__ 被覆盖。类不会自动添加self 参数。

【讨论】:

    猜你喜欢
    • 2016-08-23
    • 1970-01-01
    • 2012-03-04
    • 2021-11-11
    • 1970-01-01
    • 2011-05-05
    • 2019-05-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多