通过接受替换函数作为参数

时间:2015-11-25 17:35:16

标签: python

假设我有一个实现方法(Rectangle)的类(describe),如下所示:

class Rectangle(object):
    def __init__(self, height, width):
        self.height = height
        self.width = width

    def describe(self):
        return 'Rectangle with height {:0.2f} and width {:0.2f}'.format(float(self.height),
                                                                        float(self.width))

这可以按预期工作:

r = Rectangle(5, 3)
r.describe()
>>> 'Rectangle with height 5.00 and width 3.00'

我希望能够在实例化时指定一个代替describe的替代函数。我相信以下工作:

import functools as ft

class RectangleEnhanced(object):
    def __init__(self, height, width, description_function=None):
        self.height = height
        self.width = width
        if description_function is None:
            self.describe = self.default_describe
        else:
            self.describe = ft.partial(description_function, self)

    def default_describe(self):
        return 'Rectangle with height {:0.2f} and width {:0.2f}'.format(float(self.height),
                                                                        float(self.width))

那样:

s = RectangleEnhanced(5, 3)
s.describe()
>>> 'Rectangle with height 5.00 and width 3.00'

继续像以前一样工作,但另外:

def area_description(enh_rect):
    return 'Rectangle with area {:0.2f}'.format(float(enh_rect.height * enh_rect.width))

t = RectangleEnhanced(5, 3, area_description)
t.describe()
>>> 'Rectangle with area 15.00'

这是解决这个问题的合理方法吗?我无法想象我是第一个想要这样做的人,所以我很紧张下面的方法是次优的/ unpythonic / bad / etc。有没有"权利"处理这个的方法?

修改

这是一个更接近我的用例的例子:

class FilterableCollection(object):
    def __init__(self, items, owner, purpose, filterfunc=None):
        self.items = set(items)
        self.owner = owner
        self.purpose = purpose
        if filterfunc is None:
            self.filterfunc = lambda x: True
        else:
            self.filterfunc = ft.partial(filterfunc, self)

    def filtered(self):
        return filter(self.filterfunc, self.items)

items = ['fun_ball', 'boring_ball', 'fun_bear', 'boring_bear']
owner = 'Bill'
purpose = 'fun'

f = FilterableCollection(items, owner, purpose)
print f.filtered()

def is_applicable(self, item):
    return self.purpose in item
g = FilterableCollection(items, owner, purpose, is_applicable)
print g.filtered()

返回:

['fun_bear', 'fun_ball', 'boring_ball', 'boring_bear']
['fun_bear', 'fun_ball']

正如所料。因此,我们的想法是,当您创建FilterableCollection的特定实例时,您可以创建自定义过滤器(可能取决于该特定FitlerableCollection的其他属性),然后可以随时调用该过滤器。因此,可能会有10 FilterableCollections个浮动,并且可以通过调用.filtered方法对每个过滤器进行过滤。

我对通过继承或任何其他技术做到这一点的想法非常开放。但是在这种情况下如何适用?

2 个答案:

答案 0 :(得分:1)

您的实施是有道理的。一个类通常在其__init__采用可选参数,并且如果没有传递,则使用可感知的默认值。在您的情况下该参数是一个函数

所以这是一个很好的实现,因为你真的真的想要这样做。

然而,正如许多其他评论员指出的那样,这听起来不是一个好主意。获取简单值和使用函数覆盖此类方法之间的区别在于函数参数会影响实例的行为相同类型的实例不应该有不同的行为。

因此,使用不同类型(例如使用继承)是最直接的方法。

既然你问过,这里是你如何使用继承:

class FilterableCollection(object):
    def __init__(self, items, owner, purpose):
        self.items = set(items)
        self.owner = owner
        self.purpose = purpose

    def filtered(self):
        return self.items

class ApplicabilityFilterableCollection(FilterableCollection):
    def filtered(self):
        return [ item for item in self.items if self.purpose in item ]

f = FilterableCollection(items, owner, purpose)
print f.filtered()
g = ApplicabilityFilterableCollection(items, owner, purpose)
print g.filtered()

答案 1 :(得分:1)

  

本质是,对于每个类的实例,我希望能够   对附加到的一些数据执行某种过滤   实例。根据上下文,所需的过滤类型   可能差异很大。

您在此处描述的内容称为strategy pattern,您的示例实现几乎与pythonic一样 - Python函数是对象,相当多的设计模式需要完整的#34;仿函数"大多数主流语言中的类都是用Python中的普通函数实现的。

唯一的改进"我可以看到将摆脱部分 - Python函数确实知道如何成为实例方法:

    if description_function is None:
        self.describe = self.default_describe
    else:
        self.describe = description_function.__get__(self)

您可以阅读此内容以了解有关此行为的更多信息:https://wiki.python.org/moin/FromFunctionToMethod

并不是说这会改变任何纯粹的功能(没有双关语),但它肯定会让你看起来像PythonGuru(tm)