1

假设我们有一个类:

注意:这只是一个虚拟类。

class C(object):
    def __init__(self):
        self.a = -10
        self.b = 5
        self.c = 2
    def modify(self, **kwargs):
        for keyword in kwargs:
            vars(self)[keyword] = kwargs[keyword]
        return(self)

我们想使用这个修改方法来改变我们对象中的值:

myclass = C()
myclass = myclass.modify(a=10)

但是当我想在原来的基础上改变值时,我必须这样写:

myclass = C()
myclass = myclass.modify(a=myclass.a/10)

或者:

myclass = myclass.modify(a=abs(myclass.a))

我的问题是,有没有办法在模块中创建一个全局变量,我可以导入并将它用作当前值的占位符,所以我可以使用这个公式:

from globvars import current

myclass = C()
myclass = myclass.modify(
    a=abs(current) % current ** 2,
    b=current//2,
    c=bool(current)
)

首先,我尝试创建一个类,该类将存储它正在执行的操作和一个值,并将modify()首先查找其变量作为关键字,然后执行该函数。实际上,它仅适用于简单的情况,例如:current+10current**2. 但是当我意识到时,我想将它currenthsba(current)颜色转换器)函数一起使用,其中current指向存储在另一个对象中的对象,我只是放弃,我不能把这个写到我要去的每个类使用..

有解决方案吗?也许这很容易,我只是看不到它:)

提前感谢您的回复!

4

4 回答 4

0

这是我的旧解决方案..(有点,这是它的虚拟版本

class __current__(object):
    def do(self, e, v = None):
        c = __current__()
        c.exp = e
        if v is not None:
            c.val = v
        return(c)
    def __abs__(self):
        return(self.do(abs))
    def __rpow__(self, v):
        return(self.do(pow, v))

current = __current__()

class C(object):
    def __call__(self, **kwargs):
        for keyword, value in kwargs.iteritems():
            try:
                expression = value.exp
                try:
                    value = expression(vars(self)[keyword], value.val)
                except AttributeError:
                    value = expression(vars(self)[keyword])
            except AttributeError:
                value = value
            setattr(self, keyword, value)

以及用法:

MyObj = C()
MyObj(a = -2)
MyObj(a = abs(current))
MyObj(a = 2 ** current)
于 2013-05-04T00:01:36.673 回答
0

这是一个有效的解决方案。它不完整并且充满了非常糟糕的设计选择,但我希望它有所帮助。

class Expr(object):

    def __init__(self, op, left, right):
        self.op = op
        self.left = left
        self.right = right

    def __call__(self, current):
        l = self._replace_current(self.left, current)
        r = self._replace_current(self.right, current)
        return self._do_operation(l, r)

    def _replace_current(self, val, current):
        if val == 'current':
            return current
        elif isinstance(val, Expr):  # recurse
            return val(current)
        else:
            return val

    def _do_operation(self, l, r):
        if self.op == '+':
            return l + r
        elif self.op == '*':
            return l * r
        elif self.op == '-':
            return l - r

    def __add__(self, other):
        return self._left_op('+', other)

    def __radd__(self, other):
        return self._right_op('+', other)

    def __mul__(self, other):
        return self._left_op('*', other)

    def __rmul__(self, other):
        return self._right_op('*', other)

    def __sub__(self, other):
        return self._left_op('-', other)

    def __rsub__(self, other):
        return self._right_op('-', other)

    def _left_op(self, op, other):
        if isinstance(other, Current):
            return Expr(op=op, left=self, right='current')
        else:
            return Expr(op=op, left=self, right=other)

    def _right_op(self, op, other):
        if isinstance(other, Current):
            return Expr(op=op, left='current', right=self)
        else:
            return Expr(op=op, left=other, right=self)


class Current(Expr):

    def __init__(self):
        super(Current, self).__init__(None, None, None)

    def __call__(self, current):
        return current

    def _left_op(self, op, other):
        return Expr(op=op, left='current', right=other)

    def _right_op(self, op, other):
        return Expr(op=op, left=other, right='current')


current = Current()


class YourObj(object):

    def __init__(self, a, b):
        self.a = a
        self.b = b

    def __call__(self, **kw):
        for key, val in kw.iteritems():
            # You should probably make sure it is actually an attribute of YourObj
            if isinstance(val, Expr):
                current = self.a
                new_val = val(current)
                setattr(self, key, new_val)
            else:
                setattr(self, key, val)

您可以执行以下操作:

obj = YourObj(a=4, b=5)
obj(a=current - 4 + current * current)

这基本上是嵌入在 python 数学运算中的表达式解释器。

每当您对(如 +)使用操作时current,它将返回一个Expr(因为它覆盖__add__and __radd__),它将注册这是哪个操作,以及它的每个操作数是什么。这些表达式可以嵌套,所以如果你说current + 4 - current,它将返回Expr(op='-', left=Expr(op='+', left='current', right=4), right='current')

然后可以通过像函数一样调用表达式并将应该替换“当前”的值传递给它来评估表达式。当您评估一个表达式时,它将:

  • 用传递的值替换所有出现的“当前”
  • 递归地评估嵌套函数
  • 返回整个表达式的最终结果

当您这样做时obj(a=current + 4),将调用的__call__方法。YourObj它将评估结果的表达式current + 4并将其存储在a.

我希望这更清楚。也许我应该重命名一些“当前”以减少混乱。

于 2013-05-03T22:51:19.570 回答
0

您的modify方法可以采用要修改的属性的名称,以及采用该属性的当前值并返回其新计算值的函数。然后您可以执行以下操作:

def compute_new_value(current):
    new_value = abs(current) % current ** 2
    return new_value

myclass = C()
myclass.modify('a', compute_new_value)

对于简单的情况,lambda使其不那么冗长:

myclass.modify('a', lambda cur: cur + 4)

还有你的课:

class C(object):
    [...]
    def modify(self, attr_name, func):
        cur_value = getattr(self, attr_name)
        new_value = func(cur_value)
        setattr(self, attr_name, new_value)

编辑:我可能错过了一些东西。既然你在写myclass = myclass.modify...,该modify方法应该返回对象的副本吗?

于 2013-05-03T14:41:23.303 回答
0

在我看来,你的设计很糟糕,但你可以使用eval(). 当然,这只会让你的设计更加有味道。仍然...

class C(object):
    # ...
    def modify(self, **kwargs):
        for name, expr in kwargs.iteritems():
            setattr(self, name, eval(expr, vars(self)))

obj = C()
obj.modify(a="a+2", b="b*42")

缺点是您必须将表达式作为字符串传递。此外,通过这个简单的实现,您只能在表达式中使用在实例上定义的值(例如,您不能访问类属性、父类的任何属性或全局变量)。v当然,您可以通过以适当的顺序构建字典来添加使用类属性或全局变量甚至父类的能力:

def modify(self, **kwargs):
    vardict = {}   # allow globals and self attributes to be used in expressions
    vardict.update(globals())
    vardict.update(vars(self))
    for name, expr in kwargs.iteritems():
        value = eval(expr, v)
        setattr(self, name, eval(expr, vardict))
        vardict[name] = value

如果你想要一个current保存当前值的变量,你可以使用它(在循环内,因为它需要为每个处理的属性进行更改):

v["current"] = getattr(self, name, None)

这里最大的缺点之一是您不能轻松地从调用者的范围内访问变量,尽管我猜您可以将它们从堆栈框架中挖掘出来......呃。或者让调用者将它们插入到字符串中......双重呃。

在我看来,Morphyn 的回答是正确的做法。lambda 并不复杂

于 2013-05-03T15:24:41.670 回答