convert the result in the context manager - python

I have my own context manager class: my_context_manager, I want to convert the result to the giving output_type, can be for example str, list, int whatever, I tried to play with the __enter__ , __exit__ methods, in my_context_manager, but I didn't find how to get the variable used inside the with scope,
with my_context_manager(output_type): # output_type can be str, int etc
result = 5 + 2 #( or any other any arithmetical operation operation)

You can't manipulate the code running in the with block with a context manager.
Basically all that with does is call __enter__ and __exit__ on the object you with, so your code is (if we skip exception handling) equivalent to
_anonymous_var = my_context_manager(output_type)
_anonymous_var.__enter__()
result = 5 + 2
_anonymous_var.__exit__()
EDIT
if we replaced the 5 and the 2 with a custom object, can i modify the result object to converted based on the giving type in the context manager?
You could return the custom object from the context manager, like so:
from contextlib import contextmanager
#contextmanager
def my_context_manager(output_type):
def caster(in_value):
out_value = output_type(in_value)
print(f"cast {in_value!r} to {out_value!r}")
return out_value
yield caster
with my_context_manager(int) as c:
result = c(5) + c("2")
print(result)
with my_context_manager(str) as c:
result = c(5) + c(2)
print(result)
The output is
cast 5 to 5
cast '2' to 2
7
cast 5 to '5'
cast 2 to '2'
52
but if you don't want to return a brand new thing, you can also just modify an existing object within the with:
class MySpecialClass:
behavior = ...
#contextmanager
def behave_differently(behavior):
old_behavior = MySpecialClass.behavior
try:
MySpecialClass.behavior = behavior
yield
finally:
MySpecialClass.behavior = old_behavior
EDIT 2
Using a similar MyObject as OP's other answer, to illustrate the second example in the edit:
import operator
from contextlib import contextmanager
class MyObject:
output_type = None
def __init__(self, value):
self.value = value
#classmethod
def _convert(cls, value):
if cls.output_type is None:
return value
return cls.output_type(value)
#classmethod
def _apply(cls, fn, a, b):
return cls(fn(cls._convert(a), cls._convert(b)))
def __repr__(self):
return f"{self.__class__.__name__}({self.value!r})"
def __add__(self, other):
return self._apply(operator.add, self.value, other.value)
def __sub__(self, other):
return self._apply(operator.sub, self.value, other.value)
#classmethod
#contextmanager
def set_type(cls, output_type):
old_behavior = cls.output_type
try:
cls.output_type = output_type
yield
finally:
cls.output_type = old_behavior
print(MyObject(1) + MyObject(2.5))
with MyObject.set_type(int):
print(MyObject(1) + MyObject(2.5))
with MyObject.set_type(str):
print(MyObject(1) + MyObject(2.5))
outputs (annotated by me)
MyObject(3.5) # no conversions
MyObject(3) # everything converted to int
MyObject('12.5') # everything converted to str

well i solve it finally,
the solution is to change the add sub methods of the operand,
so if the context class has a type not None, will converted it to the giving type. otherwise it will remain the same.
class MyObject:
def __init__(self, value, ):
self.value = value
def __add__(self, other):
new_obj = MyObject(self.value + other.value)
if MyContextManager.output_type is not None:
new_obj.conversion(my_context_manager.output_type)
return new_obj
def __sub__(self, other):
new_obj = MyObject(self.value + other.value)
if MyContextManager.output_type is not None:
new_obj.conversion(my_context_manager.output_type)
return new_obj
def conversion(self, new_type):
pass
class MyContextManager:
output_type = None
def __init__(self, output_type):
self.output_type = output_type
def __enter__(self):
self.change_output(self.output_type)
def __exit__(self, exc_type, exc_val, exc_tb):
self.change_output(None)
#classmethod
def change_output(cls, output_type):
cls.output_type = output_type

Related

Getting a decorator with args to work with method: self does not exist

When I try to decorate a method with arguments in the decorate it gives me a self is not defined. if instead I change assign val as a class variable and use MyCls.val it now gives me MyCls is not defined! How do I decorate my mehtod?
def dodecorate(VAL):
def decorate(func):
def wrapped(*args,**kwargs):
res = func(*args,**kwargs)
if res == VAL:
res = "one"
return res
return wrapped
return decorate
class MyCls(object):
def __init__(self):
self.val = 1
#dodecorate(VAL = self.val)
def onefrom1(self, x):
return x
EDIT The above was an abstraction I invented to represent the original. Here is the actual code.
def retry_on_invalid_schema(MAX_RETRIES):
def retrier(func):
def wrapped(*args, **kwargs):
tries = 0
res = None
while tries < MAX_TRIES:
try:
res = func(*args, **kwargs)
assert res.has_key('by')
assert res.has_key('id')
break
except AssertionError:
res = None
time.sleep(2**tries)
tries += 1
continue
return res
return wrapped
return retrier
class Base(object):
def __init__(self):
self.MAX_RETRIES = 3
#retry_on_invalid_schema(MAX_RETRIES = self.MAX_RETRIES)
def _get_api_response(self, uri):
return json.loads(self._get(uri))
def _get(self, uri):
return requests.get(uri).text
If you want to use an existing decorator function on an instance method, note that you can redefine instance methods in __init__, calling the decorator directly rather than using the # syntax:
class MyCls(object):
def __init__(self):
self.val = 1
self.onefrom1 = dodecorate(self.val)(self.onefrom1)
def onefrom1(self, x):
return x
In use:
>>> a = MyCls()
>>> for x in range(3):
print a.onefrom1(x)
0
one
2
Instead of trying to pass val to the decorator, you can just access it via the instance. self contains the current instance and is passed to the wrapped method in the decorator, so you can access that in the decorator as the first argument.
def decorate(func):
def wrapped(instance, *args, **kwargs):
res = func(instance, *args, **kwargs)
if res == instance.val:
res = "one"
return res
return wrapped
class MyCls(object):
def __init__(self):
self.val = 1
#decorate
def onefrom1(self, x):
return x
c = MyCls()
print c.onefrom1(1)
print c.onefrom1(2)
output
one
2

python3: singledispatch in class, how to dispatch self type

Using python3.4. Here I want use singledispatch to dispatch different type in __mul__ method . The code like this :
class Vector(object):
## some code not paste
#functools.singledispatch
def __mul__(self, other):
raise NotImplementedError("can't mul these type")
#__mul__.register(int)
#__mul__.register(object) # Becasue can't use Vector , I have to use object
def _(self, other):
result = Vector(len(self)) # start with vector of zeros
for j in range(len(self)):
result[j] = self[j]*other
return result
#__mul__.register(Vector) # how can I use the self't type
#__mul__.register(object) #
def _(self, other):
pass # need impl
As you can see the code , I want support Vector*Vertor , This has Name error
Traceback (most recent call last):
File "p_algorithms\vector.py", line 6, in <module>
class Vector(object):
File "p_algorithms\vector.py", line 84, in Vector
#__mul__.register(Vector) # how can I use the self't type
NameError: name 'Vector' is not defined
The question may be How can I use class name a Type in the class's method ? I know c++ have font class statement . How python solve my problem ? And it is strange to see result = Vector(len(self)) where the Vector can be used in method body .
After have A look at http://lukasz.langa.pl/8/single-dispatch-generic-functions/
I can choose this way to implement :
import unittest
from functools import singledispatch
class Vector(object):
"""Represent a vector in a multidimensional space."""
def __init__(self, d):
self._coords = [0 for i in range(0, d)]
self.__init__mul__()
def __init__mul__(self):
__mul__registry = self.__mul__.registry
self.__mul__ = singledispatch(__mul__registry[object])
self.__mul__.register(int, self.mul_int)
self.__mul__.register(Vector, self.mul_Vector)
def __setitem__(self, key, value):
self._coords[key] = value
def __getitem__(self, item):
return self._coords[item]
def __len__(self):
return len(self._coords)
def __str__(self):
return str(self._coords)
#singledispatch
def __mul__(self, other):
print ("error type is ", type(other))
print (type(other))
raise NotImplementedError("can't mul these type")
def mul_int(self,other):
print ("other type is ", type(other))
result = Vector(len(self)) # start with vector of zeros
for j in range(len(self)):
result[j] = self[j]*other
return result
def mul_Vector(self, other):
print ("other type is ", type(other))
#result = Vector(len(self)) # start with vector of zeros
sum = 0
for i in range(0,len(self)):
sum += self._coords[i] * other._coords[i]
return sum
class TestCase(unittest.TestCase):
def test_singledispatch(self):
# the following demonstrates usage of a few methods
v = Vector(5) # construct five-dimensional <0, 0, 0, 0, 0>
for i in range(1,6):
v[i-1] = i
print(v.__mul__(3))
print(v.__mul__(v))
print(v*3)
if __name__ == "__main__":
unittest.main()
The answer is strange :
other type is <class 'int'>
[3, 6, 9, 12, 15]
other type is <class '__main__.Vector'>
55
error type is <class 'int'>
Traceback (most recent call last):
File "p_algorithms\vector.py", line 164, in <module>
print(v*3)
File "C:\Python34\lib\functools.py", line 710, in wrapper
return dispatch(args[0].__class__)(*args, **kw)
File "p_algorithms\vector.py", line 111, in __mul__
raise NotImplementedError("can't mul these type")
v.__mul__(3) can work but v*3 can't work. This is strange From my option v*3 is just the same as v.__mul__(3) .
Update after #Martijn Pieters's comment, I still want implement v*3 in class. So I try this
import unittest
from functools import singledispatch
class Vector(object):
#staticmethod
def static_mul_int(self,other):
print ("other type is ", type(other))
result = Vector(len(self)) # start with vector of zeros
for j in range(len(self)):
result[j] = self[j]*other
return result
#singledispatch
#staticmethod
def __static_mul__(cls, other):
print ("error type is ", type(other))
print (type(other))
raise NotImplementedError("can't mul these type")
__mul__registry2 = __static_mul__.registry
__mul__ = singledispatch(__mul__registry2[object])
__mul__.register(int, static_mul_int)
def __init__(self, d):
self._coords = [0 for i in range(0, d)]
self.__init__mul__()
def __init__mul__(self):
__mul__registry = self.__mul__.registry
print ("__mul__registry",__mul__registry,__mul__registry[object])
self.__mul__ = singledispatch(__mul__registry[object])
self.__mul__.register(int, self.mul_int)
print ("at last __mul__registry",self.__mul__.registry)
# #singledispatch
# def __mul__(self, other):
# print ("error type is ", type(other))
# print (type(other))
# raise NotImplementedError("can't mul these type")
def mul_int(self,other):
print ("other type is ", type(other))
result = Vector(len(self)) # start with vector of zeros
for j in range(len(self)):
result[j] = self[j]*other
return result
def __setitem__(self, key, value):
self._coords[key] = value
def __getitem__(self, item):
return self._coords[item]
def __len__(self):
return len(self._coords)
def __str__(self):
return str(self._coords)
class TestCase(unittest.TestCase):
def test_singledispatch(self):
# the following demonstrates usage of a few methods
v = Vector(5) # construct five-dimensional <0, 0, 0, 0, 0>
for i in range(1,6):
v[i-1] = i
print(v.__mul__(3))
print("type(v).__mul__'s registry:",type(v).__mul__.registry)
type(v).__mul__(v, 3)
print(v*3)
if __name__ == "__main__":
unittest.main()
This time . v.__mul__(3) have error :
Traceback (most recent call last):
File "test.py", line 73, in test_singledispatch
type(v).__mul__(v, 3)
File "/usr/lib/python3.4/functools.py", line 708, in wrapper
return dispatch(args[0].__class__)(*args, **kw)
TypeError: 'staticmethod' object is not callable
For me static method should act like the instance method.
You cannot use functools.singledispatch on methods at all, not as a decorator at least. Python 3.8 adds a new option, just for methods: functools.singledispatchmethod().
It doesn't matter that Vector isn't defined here yet; the first argument to any method is always going to be self, while you'd use single dispatch for the second argument here.
Because decorators apply to the function objects before the class object is created, you could just as well register your 'methods' as functions instead, outside of the class body, so you have access to the Vector name:
class Vector(object):
#functools.singledispatch
def __mul__(self, other):
return NotImplemented
#Vector.__mul__.register(int)
#Vector.__mul__.register(Vector)
def _(self, other):
result = Vector(len(self)) # start with vector of zeros
for j in range(len(self)):
result[j] = self[j]*other
return result
For non-supported types, you need to return the NotImplemented singleton, not raise an exception. This way Python will try the inverse operation too.
However, since the dispatch is going to key on the wrong argument (self) here anyway, you'll have to come up with your own single dispatch mechanism.
If you really want to use #functools.singledispatch you'd have to delegate to a regular function, with the arguments inversed:
#functools.singledispatch
def _vector_mul(other, self):
return NotImplemented
class Vector(object):
def __mul__(self, other):
return _vector_mul(other, self)
#_vector_mul.register(int)
def _vector_int_mul(other, self):
result = Vector(len(self))
for j in range(len(self)):
result[j] = self[j] * other
return result
As for your updates using __init__mul__: v * 3 is not translated to v.__mul__(3). It is instead translated to type(v).__mul__(v, 3), see Special method lookup in the Python datamodel reference. This always bypasses any methods set directly on the instance.
Here type(v) is Vector; Python looks up the function, it won't use a bound method here. Again, because functools.singledispatch dispatches on the first argument, always, you cannot use single dispatch directly on the methods of Vector, because that first argument is always going to be a Vector instance.
In other words, Python will not use the methods you set on self in __init__mul__; special methods are never looked up on the instance, see Special method lookup in the datamodel documentation.
The functools.singledispatchmethod() option that Python 3.8 adds uses a class as the decorator which implements the descriptor protocol, just like methods do. This lets it then handle dispatch before binding (so before self would be prepended to the argument list) and then bind the registered function that the singledispatch dispatcher returns. The source code for this implementation is fully compatible with older Python versions, so you could use that instead:
from functools import singledispatch, update_wrapper
# Python 3.8 singledispatchmethod, backported
class singledispatchmethod:
"""Single-dispatch generic method descriptor.
Supports wrapping existing descriptors and handles non-descriptor
callables as instance methods.
"""
def __init__(self, func):
if not callable(func) and not hasattr(func, "__get__"):
raise TypeError(f"{func!r} is not callable or a descriptor")
self.dispatcher = singledispatch(func)
self.func = func
def register(self, cls, method=None):
"""generic_method.register(cls, func) -> func
Registers a new implementation for the given *cls* on a *generic_method*.
"""
return self.dispatcher.register(cls, func=method)
def __get__(self, obj, cls):
def _method(*args, **kwargs):
method = self.dispatcher.dispatch(args[0].__class__)
return method.__get__(obj, cls)(*args, **kwargs)
_method.__isabstractmethod__ = self.__isabstractmethod__
_method.register = self.register
update_wrapper(_method, self.func)
return _method
#property
def __isabstractmethod__(self):
return getattr(self.func, '__isabstractmethod__', False)
and apply that to your Vector() class. You still have to register your Vector implementation for the single dispatch after the class has been created, because only then can you register a dispatch for the class:
class Vector(object):
def __init__(self, d):
self._coords = [0] * d
def __setitem__(self, key, value):
self._coords[key] = value
def __getitem__(self, item):
return self._coords[item]
def __len__(self):
return len(self._coords)
def __repr__(self):
return f"Vector({self._coords!r})"
def __str__(self):
return str(self._coords)
#singledispatchmethod
def __mul__(self, other):
return NotImplemented
#__mul__.register
def _int_mul(self, other: int):
result = Vector(len(self))
for j in range(len(self)):
result[j] = self[j] * other
return result
#Vector.__mul__.register
def _vector_mul(self, other: Vector):
return sum(sc * oc for sc, oc in zip(self._coords, other._coords))
You could of course also create a subclass first and dispatch based on that, since dispatch works for subclasses too:
class _Vector(object):
def __init__(self, d):
self._coords = [0] * d
class Vector(_Vector):
def __setitem__(self, key, value):
self._coords[key] = value
def __getitem__(self, item):
return self._coords[item]
def __len__(self):
return len(self._coords)
def __repr__(self):
return f"{type(self).__name__}({self._coords!r})"
def __str__(self):
return str(self._coords)
#singledispatchmethod
def __mul__(self, other):
return NotImplemented
#__mul__.register
def _int_mul(self, other: int):
result = Vector(len(self))
for j in range(len(self)):
result[j] = self[j] * other
return result
#__mul__.register
def _vector_mul(self, other: _Vector):
return sum(sc * oc for sc, oc in zip(self._coords, other._coords))
This is a little ugly, as you need to defer binding the implementation of Vector/Vector multiplication until after Vector is actually defined. But the idea is that the single-dispatch function needs the first argument to be of arbitrary type, so Vector.__mul__ will call that function with self as the second argument.
import functools
class Vector:
def __mul__(self, other):
# Python has already dispatched Vector() * object() here, so
# swap the arguments so that our single-dispatch works. Note
# that in general if a*b != b*a, then the _mul_by_other
# implementations need to compensate.
return Vector._mul_by_other(other, self)
#functools.singledispatch
def _mul_by_other(x, y):
raise NotImplementedError("Can't multiply vector by {}".format(type(x)))
#_mul_by_other.register(int)
def _(x, y):
print("Multiply vector by int")
#Vector._mul_by_other.register(Vector)
def _(x, y):
print("Multiply vector by another vector")
x = Vector()
y = Vector()
x * 3
x * y
try:
x * "foo"
except NotImplementedError:
print("Caught attempt to multiply by string")

Setting a get/set property in a python memoization decorator class

I have created a decorator memoization class that I am actively using for cache my calls. There are already many excellent suggestions on how to implement python memoization.
The class that I have created currently uses get and set method calls to set the cacheTimeOut. They are called getCacheTimeOut() and setCacheTimeOut(). While this is an adequate solution. I was hoping to use the #property and #cacheTimeOut.setter decorators to enable the functions to be called directly as for example cacheTimeOut=120
The problem is in the details. I do not know how to make these properties accessible in the __get__ method. The __get__ method assigns the different function calls defined within the class to functions.partial.
Here is my script example designed for Python 2.7
import time
from functools import partial
import cPickle
class memoize(object):
def __init__(self, func):
self.func = func
self._cache = {}
self._timestamps = {}
self._cacheTimeOut = 120
self.objtype = None
def __new__(cls, *args, **kwargs):
return object.__new__(cls,*args, **kwargs)
def __get__(self, obj, objtype=None):
"""Used for object methods where decorator has been placed before methods."""
self.objtype = objtype
fn = partial(self, obj)
fn.resetCache = self.resetCache
fn.getTimeStamps = self.getTimeStamps
fn.getCache = self.getCache
fn._timestamps = self._timestamps
fn.setCacheTimeOut = self.setCacheTimeOut
fn.getCacheTimeOut = self.getCacheTimeOut
return fn
def __argsToKey(self, *args, **kwargs):
args = list(args)
for x, arg in enumerate(args): # remove instance from
if self.objtype:
if isinstance(arg, self.objtype):
args.remove(arg)
str = cPickle.dumps(args, 1)+cPickle.dumps(kwargs, 1)
return str
def __call__(self, *args, **kwargs):
"""Main calling function of decorator."""
key = self.__argsToKey(*args, **kwargs)
now = time.time() # get current time to query for key
if self._timestamps.get(key, now) > now:
return self._cache[key]
else:
value = self.func(*args, **kwargs)
self._cache[key] = value
self._timestamps[key] = now + self._cacheTimeOut
return value
def __repr__(self):
'''Return the function's docstring.'''
return self.func.__doc__
def resetCache(self):
"""Resets the cache. Currently called manually upon request."""
self._cache = {}
self._timestamps = {}
def getCacheTimeOut(self):
"""Get the cache time out used to track stale data."""
return self._cacheTimeOut
def setCacheTimeOut(self, timeOut):
"""Set the cache timeout to some other value besides 120. Requires an integer value. If you set timeOut to zero you are ignoring the cache"""
self._cacheTimeOut = timeOut
def getCache(self):
"""Returns the cache dictionary."""
return self._cache
def getTimeStamps(self):
"""Returns the encapsulated timestamp dictionary."""
return self._timestamps
#property
def cacheTimeOut(self):
"""Get cacheTimeOut."""
return self._cacheTimeOut
#cacheTimeOut.setter
def cacheTimeOut(self, timeOut):
"""Set cacheTimeOut."""
self._cacheTimeOut = timeOut
memoize
def increment(x):
increment.count+=1
print("increment.count:%d, x:%d"%(increment.count, x))
x+=1
return x
increment.count = 0 # Define the count to track whether calls to increment vs cache
class basic(object):
def __init__(self):
self.count = 0
#memoize
def increment(self, x):
self.count+=1
print("increment.count:%d, x:%d"%(increment.count, x))
x+=1
return x
def main():
print increment(3)
print increment(3)
# What I am actually doing
print increment.getCacheTimeOut() # print out default of 120
increment.setCacheTimeOut(20) # set to 20
print increment.getCacheTimeOut() # verify that is has been set to 120
# What I would like to do and currently does not work
print increment.cacheTimeOut
# Assign to property
increment.cacheTimeOut = 20
myObject = basic()
print myObject.increment(3)
print myObject.count
print myObject.increment(3)
print myObject.count
print myObject.increment(4)
print myObject.count
####### Unittest code.
import sys
import time
import unittest
from memoize import memoize
class testSampleUsages(unittest.TestCase):
# """This series of unit tests is to show the user how to apply memoize calls."""
def testSimpleUsageMemoize(self):
#memoize
def increment(var=0):
var += 1
return var
increment(3)
increment(3)
def testMethodBasedUsage(self):
"""Add the #memoize before method call."""
class myClass(object):
#memoize
def increment(self,var=0):
var += 1
return var
#memoize
def decrement(self, var=0):
var -=1
return var
myObj = myClass()
myObj.increment(3)
myObj.increment(3)
myObj.decrement(6)
myObj.decrement(6)
def testMultipleInstances(self):
#memoize
class myClass(object):
def __init__(self):
self.incrementCountCalls = 0
self.decrementCountCalls = 0
self.powCountCall = 0
# #memoize
def increment(self,var=0):
var += 1
self.incrementCountCalls+=1
return var
# #memoize
def decrement(self, var=0):
self.decrementCountCalls+=1
var -=1
return var
def pow(self, var=0):
self.powCountCall+=1
return var*var
obj1 = myClass() # Memoizing class above does not seem to work.
obj2 = myClass()
obj3 = myClass()
obj1.increment(3)
obj1.increment(3)
#obj2.increment(3)
#obj2.increment(3)
#obj3.increment(3)
#obj3.increment(3)
obj1.pow(4)
obj2.pow(4)
obj3.pow(4)
There's no way to attach a property to a single instance. Being descriptors, propertys must be part of a class definition in order to function. That means you can't easily add them to the partial object you create in __get__.
Now, you could create a class of your own to reimplement the behavior of partial with your added property. However, I suspect the limitation is actually to your benefit. If memo is applied to a method, its state is shared by all instances of the class (and perhaps even instances of subclasses). If you allow the caching details to be adjusted through instances, you might confuse users with cases like:
obj1 = basic()
print obj1.increment.getCacheTimeout() # prints the initial value, e.g. 120
obj2 = basic()
obj2.increment.setCacheTimeOut(20) # change the timeout value via another instance
print obj1.increment.getCacheTimeout() # the value via the first instance now prints 20
I suggest that you make the memoization-related interfaces of decorated methods accessible only through the class, not through instances. To make that work, you need to update your __get__ method to work if obj is None. It can simply return self:
def __get__(self, obj, objtype=None):
if obj is None:
return self
self.objtype = objtype
return partial(self, obj) # no need to attach our methods to the partial anymore
With this change, using a property on the memo via the class works:
basic.increment.cacheTimeOut = 20 # set property of the "unbound" method basic.increment
There is actually a way to accomplish this - by rebinding the decorator as instance-object with a call-method
class Helper(object):
def __init__(self, d, obj):
self.d = d
self.obj = obj
self.timeout = 0
def __call__(self, *args, **kwargs):
print self, self.timeout
return self.d.func(self.obj, *args, **kwargs)
class decorator(object):
def __init__(self, func):
self.func = func
self.name = func.__name__
def __get__(self, obj, clazz):
if object is not None:
obj.__dict__[self.name] = Helper(self, obj)
return obj.__dict__[self.name]
class Foo(object):
#decorator
def bar(self, args):
return args * 2
f = Foo()
g = Foo()
f.bar.timeout = 10
g.bar.timeout = 20
print f.bar(10)
print g.bar(20)
HTH

implementing add and iadd for custom class in python?

I am writing a Queue class that wraps list for most of its operations. But I do not sublcass from list, since I do not want to provide all the list API's. I have my code pasted below. The add method seems to work fine, but iadd seems to go wrong, it is printing none.
Here is the code:
import copy
from iterator import Iterator
class Abstractstruc(object):
def __init__(self):
assert False
def __str__(self):
return "<%s: %s>" %(self.__class__.__name__,self.container)
class Queue(Abstractstruc,Iterator):
def __init__(self,value=[]):
self.container=[]
self.size=0
self.concat(value)
def add(self, data):
self.container.append(data)
def __add__(self,other):
return Queue(self.container + other.container)
def __iadd__(self,other):
for i in other.container:
self.add(i)
def remove(self):
self.container.pop(0)
def peek(self):
return self.container[0]
def __getitem__(self,index):
return self.container[index]
def __iter__(self):
return Iterator(self.container)
def concat(self,value):
for i in value:
self.add(i)
def __bool__(self):
return len(self.container)>0
def __len__(self):
return len(self.container)
def __deepcopy__(self,memo):
return Queue(copy.deepcopy(self.container,memo))
if __name__=='__main__':
q5 = Queue()
q5.add("hello")
q6 = Queue()
q6.add("world")
q5 = q5+q6
print q5
q5+=q6
print q5
Output:
<Queue: ['hello', 'world']>
None
__iadd__ needs to return self when adding in-place:
def __iadd__(self,other):
for i in other.container:
self.add(i)
return self
__iadd__ needs to return the resulting object; for immutable types the new object, for mutable types, self. Quoting the in-place operator hooks documentation:
These 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).

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