elegant way to pass operator to attribute in python - python

Basically my Question: Is there an elegant way, if I have a given Container class
class Container:
def __init__(self, value):
self.value = value
to pass any operator to the value parameter of the Container class?
So one obvious solution would be to override any single operator on its own. So for example for + I could do:
class Container:
def __init__(self, value):
self.value = value
def __add__(self, other):
return self.value + other
so that for example
c1 = Container(1)
c1 += 1
print(c1)
would result in
2
and
c2 = Container("ab")
c2 += "c"
print(c2)
would result in
abc
So what I could do is to override all arithmetic built-in functions from python. But my Question is, is there a more elegant (shorter) way to do this for any operator?

You can factor out some of the more repetitive boilerplate:
import operator
class Container:
def __init__(self, value):
self.value = value
def _generic_op(f):
def _(self, other):
return f(self.value, other)
return _
__add__ = _generic_op(operator.add)
__sub__ = _generic_op(operator.sub)
__mul__ = _generic_op(operator.mul)
# etc
# No need for _generic_op as a class attribute
del _generic_op

If the number of overloads are smaller than the different classes you want to overload in, you can define saperate class for each overload and inherit it. So,
class Adder:
def __add__(self, val):
self.value += val
return self
class Subtractor:
def __sub__(self, val):
self.value -= val
return self
class Foo(Adder, Subtractor):
def __init__(self, value):
self.value = value
foo = Foo(5)
foo -= 3
foo += 7
print(foo.value)
But I am against doing such operations between custom DataTypes and native dataTypes. Consider this just a simple example that doesn't follow good coding principles in that sense.
If incase you still want to allow adding int/float directly, you might also want to handle Foo class objects inside too.

Related

Using property to return a specific list entry

Hy,
I have a small class with only one attribute, which is a list with four elements. I want to make attributes for each object of the list with the help of property, but I don't want to write a setter and a getter method for each element. Currently I implemented it in the following way.
class MyClass:
def __init__(self):
self.my_list = [None in range(4)]
def __get_my_list_x(self, x):
return self.my_list[x]
def __set_my_list_x(self, val, x):
self.my_list[x] = val
def get_my_list_0(self):
return self.__get_my_list_x(x=0)
def set_my_list_0(self, val):
self.__set_my_list_x(val, x=0)
# continue getter and setter methods for position 1, 2 and 3
# of my list
my_list_0 = property(get_my_list_0, set_my_list_0)
my_list_1 = property(get_my_list_1, set_my_list_1)
my_list_2 = property(get_my_list_2, set_my_list_2)
my_list_3 = property(get_my_list_3, set_my_list_3)
At the moment I'm violating the Don't repeat yourself principle, because I have to write the getter and setter methods for my_list_0 to my_list_3. Is there a way to directly call the methods __get_my_list_x and __set_my_list_x in property() and specify the x argument?
I hope you guys get my question.
Have a nice day.
There are a lot of different solutions possible depending on your exact situation outside of this probably oversimplified example.
The best solution if you need to use actual attributes is probably to define your own custom descriptors (e.g. what property does under the hood):
class MyListIndexer:
def __init__(self, index):
self.index = index
def __get__(self, instance, owner):
return instance.my_list[self.index]
def __set__(self, instance, value):
instance.my_list[self.index] = value
class MyClass:
def __init__(self):
self.my_list = [None for _ in range(4)]
my_list_0 = MyListIndexer(0)
my_list_1 = MyListIndexer(1)
You can also add another parameter to MyListIndexer specifying the name of the attribute with help of getattr.
However, consider not using attributes at all and instead providing something like direct item access with __getitem__/__setitem__:
class MyClass:
def __init__(self):
self.my_list = [None for _ in range(4)]
def __setitem__(self, key, value):
self.my_list[key] = value
def __getitem__(self, item):
return self.my_list[item]
The extreme general solution that might have unexpected consequences and should only be used if there is no other solution is to use the __getattr__/__setattr__ functions:
class MyClass:
def __init__(self):
self.my_list = [None for _ in range(4)]
def __getattr__(self, item):
if item.startswith("my_list_"):
val = int(item[8:])
return self.my_list[val]
else:
return super(MyClass, self).__getattr__(item)
def __setattr__(self, key, value):
if key.startswith("my_list_"):
ind = int(key[8:])
self.my_list[ind] = value
else:
super(MyClass, self).__setattr__(key, value)

Should class method __iadd__ always preserve the type class?

I have looked for an explicit answer, but the closest result was in the python docs here. It says:
These [augmented arithmetic assignments] methods should attempt to do the operation in-place (modifying self) and return the result (which could be, but does not have to be, self).
This implies the self (and the type) are unchanged, but is really up to the class developer to determine or should an attempt be make to always maintain the type?
I'm aware that should is an opinion question. What I'm looking for is better documented evidence on expected result.
Take the class NumString below.
class NumString:
"""A class where the number is represented as a string.
other methods omitted for brevity.
"""
def __init__(self, value):
self.value = str(value)
def __str__(self):
return self.value
def __int__(self):
return int(self.value))
def __add__(self, other):
if '.' in self.value:
return float(self) + other
return int(self) + other
def __iadd__(self, other):
self.value = str(self + other)
return self

How to return an instance's attribute by default

In my code, I have a data store with multiple variables set to instances of a class similar to that below. (The reason is that this Interval class has lots of operator overriding functions).
class Interval(object):
def __init__(self, value):
self.value = value
data_store.a = Interval(1)
I want any references to data_store.a to return self.value rather than the Interval instance. Is this possible?
As an alternative to Malik's answer, you could make a a #property, the Pythonic equivalent of get and set for managing access to internal attributes:
class DataStore(object):
def __init__(self):
self.a = Interval(1)
#property
def a(self):
return self._a.value
#a.setter
def a(self, value):
self._a = value
Here _a is a private-by-convention attribute that stores the Interval instance. This works as you want it:
>>> store = DataStore()
>>> store.a
1
You need to extend your data store whose attribute is an interval object:
class DataStore(object):
def __init__(self):
self.a = Interval(1)
def __getattribute__(self, attr):
if attr == 'a':
return object.__getattribute__(self, 'a').value
if attr != 'a':
return object.__getattribute__(self, attr)

Overwrite __sub__ for a-b when 'b' is my class

I have a class foo that is essentially a float with some extra attributes attached. I can overwrite its __sub__ method so that I can do subtraction one direction, but I can't figure out how to do it the other way:
class foo():
def __init__(self, value, otherstuff):
self.value = value
self.otherstuff = otherstuff
def __sub__(self, other):
return self.value - other
a = 5
b = foo(12, 'blue')
print b-a # this works fine and returns 7
print a-b # I want this to return -7 but it obviously doesn't work
Is there a way to do this?
A general solution for add, sub, mul, div would be ideal, but sub and div are most pressing since they're not reversible.
You just need to override __rsub__, for right-hand side subtraction:
class foo():
def __init__(self, value, otherstuff):
self.value = value
self.otherstuff = otherstuff
def __sub__(self, other):
return self.value - other
def __rsub__(self, other):
return other - self.value
Output:
print(b - a)
7
print(a - b)
-7
There are similar methods like __radd__, __rmul__ for other operations.

Is it possible to create a variable as a placeholder for 'current' value of a class in python?

Let's say we have a class:
NOTE: this is a dummy class only.
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)
And we want to use this modify method to change values in our object:
myclass = C()
myclass = myclass.modify(a=10)
But when I want to change the value based on the original one, I have to write this:
myclass = C()
myclass = myclass.modify(a=myclass.a/10)
Or:
myclass = myclass.modify(a=abs(myclass.a))
My question is, is there a way, to create a global variable in a module, that I can import and use it as a placeholder for current value, so I can use this formula:
from globvars import current
myclass = C()
myclass = myclass.modify(
a=abs(current) % current ** 2,
b=current//2,
c=bool(current)
)
First I tried to a create a class, which will store the operation it is taking and a value, and modify() will look first for its variable as a keyword and then execute the function. Actually it is only working for simple situations like: current+10 or current**2.
But when I realised, I want to use this current for example with an hsba(current) (color converter) function, where current is pointing to an object stored in an other object, I just give up, I can't write this to every class I'm going to use..
Is there a solution for this? Maybe it's quite easy, I just can't see it :)
Thanks in advance for replies!
Here is a working solution. It is not complete and full of pretty bad design choices, but I hope it helps.
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)
And you can do something like:
obj = YourObj(a=4, b=5)
obj(a=current - 4 + current * current)
This is basically an expression interpreter embedded in python's math operations.
Whenever you use an operation on current (like +), it will return an Expr (because it overrides __add__ and __radd__) that will register which operation this is, and what are each of its operands. These expressions can be nested, so if you say current + 4 - current, it will return Expr(op='-', left=Expr(op='+', left='current', right=4), right='current').
An expression can then be evaluated by calling it like a function and passing it the value that should replace 'current'. When you evaluate an expression, it will:
replace all the occurences of 'current' by the value passed
recursively evaluate the nested functions
return the end result of the whole expression
When you do obj(a=current + 4), the __call__ method of YourObj is called. It will evaluate the expression resulting of current + 4 and store it in a.
I hope this is clearer. Maybe I should rename some of the 'current' to make it less confusing.
Your modify method could take the name of the attribute to modify, and a function that takes the current value of the attribute and returns its new computed value. Then you can do something like:
def compute_new_value(current):
new_value = abs(current) % current ** 2
return new_value
myclass = C()
myclass.modify('a', compute_new_value)
For simple cases, lambda makes it less verbose:
myclass.modify('a', lambda cur: cur + 4)
And your class:
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)
Edit: I may have missed something. Since you're writing myclass = myclass.modify..., should the modify method return a copy of the object ?
You have a poor design, in my opinion, but you could do this using eval(). Of course, that just makes your design smell even more. Still...
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")
The downside is that you have to pass the expressions as strings. Also, with this simple implementation, you can only use values defined on the instance in the expression (e.g., you cant access class attributes, or any attributes of parent classes, or globals). You could add the ability to use class attributes or globals and even parent classes by building the v dictionary in the appropriate order, of course:
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
If you want a current variable that holds the current value, you could use this (inside the loop, since it needs to change for each attribute processed):
v["current"] = getattr(self, name, None)
One of the biggest drawbacks here is that you can't easily access variables from the caller's scope, although you could dig them out of the stack frame I guess... ugh. Or make the caller interpolate those into the string... double ugh.
Morphyn's answer is the proper way to do it, in my opinion. A lambda is hardly complicated
This was my old solution.. (sort of, this a dummy version of it)
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)
And the usage:
MyObj = C()
MyObj(a = -2)
MyObj(a = abs(current))
MyObj(a = 2 ** current)

Categories

Resources