假设我们有一个班级:
注意:这只是一个虚拟类。
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+10
或current**2
。
但是当我意识到,我希望使用此current
例如hsba(current)
(颜色转换器)函数,其中current
指向存储在其中的对象另一个对象,我只是放弃了,我不能把它写给我将要使用的每个类......
有解决方案吗?也许这很容易,我只是看不到它:)
提前感谢您的回复!
答案 0 :(得分: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
方法是否应该返回对象的副本?
答案 1 :(得分: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)
这里最大的缺点之一就是你无法轻易地从调用者范围访问变量,尽管你可以将它们从堆栈框架中挖出来,我猜...呃。或者让调用者将这些插入到字符串中...... double ugh。
在我看来,Morphyn的答案是正确的方法。 lambda很难复杂
答案 2 :(得分: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__
和__radd__
),这将记录此操作是什么,它的每个操作数是什么。这些表达式可以嵌套,因此如果您说current + 4 - current
,它将返回Expr(op='-', left=Expr(op='+', left='current', right=4), right='current')
。
然后可以通过将其称为函数并将其传递给应替换“current”的值来计算表达式。评估表达式时,它将:
执行obj(a=current + 4)
时,会调用__call__
的{{1}}方法。它将评估YourObj
的结果表达式并将其存储在current + 4
。
我希望这更清楚。也许我应该重命名一些'当前',以减少它的混乱。
答案 3 :(得分: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)