3

算術演算が適用された後、指定された範囲内の値を制限するオブジェクトを実装したいと思います。以下のコードは正常に機能しますが、メソッドを無意味に書き直しています。確かに、これを行うためのよりエレガントな方法があります。メタクラスは進むべき道ですか?

def check_range(_operator):
    def decorator1(instance,_val):
        value =  _operator(instance,_val)
        if value > instance._upperbound:
            value = instance._upperbound
        if value < instance._lowerbound:
            value = instance._lowerbound
        instance.value = value
        return Range(value, instance._lowerbound, instance._upperbound)
    return decorator1

class Range(object):
    '''
    however you add, multiply or divide, it will always stay within boundaries
    '''
    def __init__(self, value, lowerbound, upperbound):
        '''

        @param lowerbound:
        @param upperbound:
        '''
        self._lowerbound = lowerbound
        self._upperbound = upperbound
        self.value = value

    def init(self):
        '''
        set a random value within bounds
        '''
        self.value = random.uniform(self._lowerbound, self._upperbound)

    def __str__(self):
        return self.__repr__()

    def __repr__(self):
        return "<Range: %s>" % (self.value)

    @check_range
    def __mul__(self, other):
        return self.value * other

    @check_range
    def __div__(self, other):
        return self.value / float(other)

    def __truediv__(self, other):
        return self.div(other)     

    @check_range
    def __add__(self, other):
        return self.value + other

    @check_range
    def __sub__(self, other):
        return self.value - other
4

2 に答える 2

2

メタクラスを使用してデコレータを関数名のセットに適用することは可能ですが、これがあなたの場合の方法ではないと思います。構文を使用して、関数ごとにデコレータをクラス本体に適用すること@decoratorは、非常に良いオプションだと思います。(デコレータにバグがあると思いますが、ところで、おそらくinstance.value何も設定したくないでしょう。算術演算子は通常、オペランドを変更しません)。

私があなたの状況で使用する可能性のある別のアプローチは、デコレータをすべて一緒に回避するようなもので、次のようなことを行うことです。

import operator

class Range(object):

    def __init__(self, value, lowerbound, upperbound):
        self._lowerbound = lowerbound
        self._upperbound = upperbound
        self.value = value

    def __repr__(self):
        return "<Range: %s>" % (self.value)

    def _from_value(self, val):
        val = max(min(val, self._upperbound), self._lowerbound)
        # NOTE: it's nice to use type(self) instead of writing the class
        # name explicitly; it then continues to work if you change the
        # class name, or use a subclass
        return type(self)(val, rng._lowerbound, rng._upperbound)

    def _make_binary_method(fn):
        # this is NOT a method, just a helper function that is used
        # while the class body is being evaluated
        def bin_op(self, other):
            return self._from_value(fn(self.value, other))
        return bin_op

    __mul__ = _make_binary_method(operator.mul)
    __div__ = _make_binary_method(operator.truediv)
    __truediv__ = __div__
    __add__ = _make_binary_method(operator.add)
    __sub__ = _make_binary_method(operator.sub)

rng = Range(7, 0, 10)
print rng + 5
print rng * 50
print rng - 10
print rng / 100

印刷

<Range: 10>
<Range: 10>
<Range: 0>
<Range: 0.07>

この状況ではメタクラスを使用しないことをお勧めしますが、これが1つの方法です。メタクラスは便利なツールです。興味がある場合は、本当に必要なときにメタクラスを使用する方法を理解しておくと便利です。

def check_range(fn):
    def wrapper(self, other):
        value = fn(self, other)
        value = max(min(value, self._upperbound), self._lowerbound)
        return type(self)(value, self._lowerbound, self._upperbound)
    return wrapper

class ApplyDecoratorsType(type):
    def __init__(cls, name, bases, attrs):
        for decorator, names in attrs.get('_auto_decorate', ()):
            for name in names:
                fn = attrs.get(name, None)
                if fn is not None:
                    setattr(cls, name, decorator(fn))

class Range(object):
    __metaclass__ = ApplyDecoratorsType
    _auto_decorate = (
            (check_range, 
             '__mul__ __div__ __truediv__ __add__ __sub__'.split()),
        )

    def __init__(self, value, lowerbound, upperbound):
        self._lowerbound = lowerbound
        self._upperbound = upperbound
        self.value = value

    def __repr__(self):
        return "<Range: %s>" % (self.value)

    def __mul__(self, other):
        return self.value * other

    def __div__(self, other):
        return self.value / float(other)

    def __truediv__(self, other):
        return self / other

    def __add__(self, other):
        return self.value + other

    def __sub__(self, other):
        return self.value - other
于 2010-07-07T02:10:49.550 に答える
1

メタクラスについて賢明に言われているように、メタクラスが必要かどうか疑問に思うなら、必要ありません

私はあなたの問題を完全には理解していませんが、私はBoundedValueクラスを作成し、あなたが提案しているクラスにそのクラスのインスタンスのみを作成します。

 class BoundedValue(object):
    default_lower = 0
    default_upper = 1
    def __init__(self, upper=None, lower=None):
        self.upper = upper or BoundedValue.default_upper
        self.lower = lower or BoundedValue.default_lower
    @property
    def val(self):
        return self._val
    @val.setter
    def val(self, value):
        assert self.lower <= value <= self.upper
        self._val = value


v = BoundedValue()
v.val = 0.5 # Correctly assigns the value 0.5
print v.val # prints 0.5
v.val = 10  # Throws assertion error

もちろん、assert探している実際の動作に合わせてイオンを変更することもできます(そして変更する必要があります)。また、コンストラクターを変更して、初期化値を含めることもできます。私はそれをプロパティを介して建設後の割り当てにすることを選択しましたval

このオブジェクトを取得したら、クラスを作成して、floatsまたはintsの代わりにBoundedValueインスタンスを使用できます。

于 2010-07-07T00:40:00.437 に答える