繁体   English   中英

装饰器以更改功能行为

[英]Decorator to alter function behavior

我发现我有两个不相关的函数,它们以不同的方式实现相同的行为。 我现在想知道是否有办法通过装饰器来有效地处理此问题,从而避免在行为被添加到其他地方时一遍又一遍地编写相同的逻辑。

从本质上讲,我在两个不同的类中有两个函数,它们具有一个称为exact_match的标志。 这两个函数都会检查它们所属的对象中是否存在某种类型的对等。 exact_match标志强制用于精确检查浮点比较,而不是带有公差。 您可以在下面查看我的操作方法。

def is_close(a, b, rel_tol=1e-09, abs_tol=0.0):
    return abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)


def _equal(val_a, val_b):
"""Wrapper for equality test to send in place of is_close."""
    return val_a == val_b

    @staticmethod
def get_equivalence(obj_a, obj_b, check_name=True, exact_match=False):
    equivalence_func = is_close
    if exact_match:
        # If we're looking for an exact match, changing the function we use to the equality tester.
        equivalence_func = _equal

    if check_name:
        return obj_a.name == obj_b.name

    # Check minimum resolutions if they are specified
    if 'min_res' in obj_a and 'min_res' in obj_b and not equivalence_func(obj_a['min_res'], obj_b['min_res']):
        return False

    return False

如您所见,标准过程让我们在不需要完全匹配时使用is_close函数,但在我们需要时将其调出。 现在,另一个功能也需要相同的逻辑,换出该功能。 当我知道可能需要换出特定的函数调用时,是否可以使用装饰器或类似方法来处理这种类型的逻辑?

不需要装饰器; 只需将所需的函数作为参数传递给get_equivalence (现在只不过是应用该参数的包装器而已)。

def make_eq_with_tolerance(rel_tol=1e-09, abs_tol=0.0):
    def _(a, b):
        return abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)
    return _    

# This is just operator.eq, by the way
def _equal(val_a, val_b-):
    return val_a == val_b

def same_name(a, b):
    return a.name == b.name

现在, get_equivalence接受三个参数:两个要比较的对象,以及在这两个参数上调用的函数。

@staticmethod
def get_equivalence(obj_a, obj_b, equivalence_func):

    return equivalence_func(obj_a, obj_b)

一些示例调用:

get_equivalence(a, b, make_eq_with_tolerance())
get_equivalence(a, b, make_eq_with_tolerance(rel_tol=1e-12))  # Really tight tolerance
get_equivalence(a, b, _equal)
get_equivalence(a, b, same_name)

我想出了一个可能不太正确的替代解决方案,但是答案让我按我原本的意愿来解决问题。

我的解决方案使用了一个实用程序类,该实用程序类可用作类的成员或该类的mixin,以方便的方式提供实用程序功能。 下面,函数_equalsis_close在其他地方定义,因为它们的实现is_close重点之列。

class EquivalenceUtil(object):
    def __init__(self, equal_comparator=_equals, inexact_comparator=is_close):
        self.equals = equal_comparator
        self.default_comparator = inexact_comparator

    def check_equivalence(self, obj_a, obj_b, exact_match=False, **kwargs):
        return self.equals(obj_a, obj_b, **kwargs) if exact_match else self.default_comparator(obj_a, obj_b, **kwargs)

这是一个简单的类,可以像这样使用:

class BBOX(object):
    _equivalence = EquivalenceUtil()

    def __init__(self, **kwargs):
        ...

    @classmethod
    def are_equivalent(cls, bbox_a, bbox_b, exact_match=False):
        """Test for equivalence between two BBOX's."""
        bbox_list = bbox_a.as_list
        other_list = bbox_b.as_list
        for _index in range(0, 3):
            if not cls._equivalence.check_equivalence(bbox_list[_index], 
                                                      other_list[_index], 
                                                      exact_match=exact_match):
            return False
        return True

对于用户如何在幕后检查事物,此解决方案对用户而言更加不透明,这对我的项目很重要。 此外,它非常灵活,可以在一个类中以多种方式和位置重用,并且可以轻松地添加到新类中。

在我的原始示例中,代码可以变成这样:

class TileGrid(object):

    def __init__(self, **kwargs):
        ...

    @staticmethod
    def are_equivalent(grid1, grid2, check_name=False, exact_match=False):
        if check_name:
            return grid1.name == grid2.name
        # Check minimum resolutions if they are specified
        if 'min_res' in grid1 and 'min_res' in grid2 and not cls._equivalence.check_equivalence(grid1['min_res'], grid2['min_res'], exact_match=exact_match):
            return False

        # Compare the bounding boxes of the two grids if they exist in the grid
        if 'bbox' in grid1 and 'bbox' in grid2:
            return BBOX.are_equivalent(grid1.bbox, grid2.bbox, exact_mach=exact_match)

        return False

在一般情况下,我不推荐这种方法,因为我不禁觉得它有一些代码味道,但是它确实满足了我的需要,并且将解决当前代码库的许多问题。 我们有特定的要求,这是一个特定的解决方案。 chepner的解决方案可能最适合让用户决定功能应如何测试等效性的一般情况。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM