implementing add and iadd for custom class in python? - 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).

Related

convert the result in the context manager

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

Can I use Python's functools #cache based on identity?

I would like to have a Python #cache decorator based on identity, not __hash__/__equal.
That is to say, I would like the cached value for an argument ka NOT to be used for a different object ka2, even if ka == ka2.
Is there a way to do that?
In code:
from functools import cache
class Key:
def __init__(self, value):
self.value = value
def __eq__(self, another):
print(f"__eq__ {self.value}, {another.value}")
return another.value == self.value
def __hash__(self):
print(f"__hash__ {self.value}")
return hash(self.value)
def __repr__(self):
return self.value
i = 0
#cache
def foo(key):
global i
i += 1
print(f"Computing foo({key}) = {i}")
return i
ka = Key('a')
ka2 = Key('a')
print(f"foo(ka): {foo(ka)}")
print(f"foo(ka2): {foo(ka2)}") # I would like the cached value for ka NOT to be used even though ka2 == ka.
Make a wrapper like Key that compares by the identity of its wrapped object, and wrap your caching function in a helper that uses the wrapper:
class Id:
__slots__="x",
def __init__(self,x): self.x=x
def __hash__(self): return id(self.x)
def __eq__(self,o): return self.x is o.x
def cache_id(f):
#functools.cache
def id_f(i): return f(i.x)
#functools.wraps(f)
def call(x): return id_f(Id(x))
return call
#cache_id
def foo(key): …

Access variable value from decorator after decorated function executes

def memoize(fn):
memory=dict()
def inner(inpt):
if not inpt in memory:
memory[inpt] = fn(inpt)
return memory[inpt]
return inner
#memoize
def expected_rounds(picks_to_go):
#algorithm....
ans += expected_rounds(new_picks_to_go) #some recursion
#algorithm....
return ans
How to access (and even print) variable memory in memoize decorator after function will successfully end execution and return answer?
my idea that I created after posting question it uses class but I would like to stay with function decorator:
#class callable that can be used as decorator
class Memoize:
def __init__(self):
self.memory = {}
# self.fn = None
def set_fn(self, fn):
self.fn = fn
return self # this line is important
def __call__(self, *args):
if args not in self.memo:
self.memory[args] = self.fn(*args)
return self.memory[args]
memo1 = Memoize()
#memo1.set_fn
def expected_rounds(picks_to_go):
#something
return ans
expected_rounds(some_input)
memo1.memory # <- access to memory possible

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

instance variables

I was wondering if it is possible to create a function foo in python so that
def calulate (self, input):
input = #some stuff
def foo2(self):
self.calculate(self.var1)
self.calculate(self.var2)
or do you have to do this
def calculation(self):
output=#some stuff
return output
def foovar1(self):
self.var1=self.calculation()
self.var2=self.calculation()
I really don't want to have to do this because it would mean creating many more functions
In Python, you can mutate function arguments, but you can't rebind them in the caller's scope directly. You could pass the instance member name:
def foo(self, inputname):
setattr(self, inputname, #some stuff)
def foo2(self):
self.foo('var1')
self.foo('var2')
Alternately, if self.var1 is a mutable object e.g. a list you could write:
def foo (self, input):
input[:] = #some stuff
def foo2(self):
self.foo(self.var1)
self.foo(self.var2)
This works because you're mutating the list object (by assigning to a full slice) rather than rebinding it (a bare =).
Another solution could be to have a specially crafted container.
class Container(object):
def __init__(self, value=None):
self.value = value
and then use it in another class:
def foo(self, container):
container.value = ...
def foo2(self):
self.foo(self.var1)
self.foo(self.var2)

Categories

Resources