[英]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,以方便的方式提供實用程序功能。 下面,函數_equals
和is_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.