wrapper class with __enter__/__exit__ in python3 - python-3.x

I have a code that perfectly works in Python 2, but it doesn't work in Python 3.
There is an aggregator class data and a few classes to work with specific data formats.
class data():
def __init__(self, file, format="x"):
if format == "x":
self.data = xdata(file)
elif format == "y":
self.data = ydata(file)
# Redirect functions to the specific class
self.__enter__ = self.data.__enter__
self.__exit__ = self.data.__exit__
class xdata():
def __init__(self, file):
#do something
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
#do something
class ydata():
def __init__(self, file):
#do something
def __enter__(self):
return self
def __exit__(self,exc_type, exc_value, traceback):
#do something
In python2 I was able to execute the following code without any errors,
with data("newfile.x") as sd:
#do something with data
but python3 returns an error AttributeError: __enter__
Any ideas on how to fix it?

__enter__ and __exit__ will be resolved as descriptors, which means that resolution bypasses the attributes of the class. You can provide your own descriptors for __enter__ and __exit__ using property:
class xdata():
def __init__(self, file):
self.attr = 'x'
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
class ydata():
def __init__(self, file):
self.attr = 'y'
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
class data():
def __init__(self, file, format="x"):
if format == "x":
self.data = xdata(file)
elif format == "y":
self.data = ydata(file)
# Redirect functions to the specific class
#property
def __enter__(self):
return self.data.__enter__
#property
def __exit__(self):
return self.data.__exit__
with data("", "x") as d:
print(d.attr) # x
with data("", "y") as d:
print(d.attr) # y

Related

With open inside of a class

Hello I am doing an assignment and I have a similar problem as to here but in this case I am unable to change the file = logsys(file_location) part due to the assignment rules. Is there any way I can do the "with" part inside of the class?
My code is shown below
doc = write("test_2.txt")
class HtmlTable:
def __init__(self, filename):
self.filename = filename
self.fp = None
def tag(self, text):
self.fp.write('<' + text+ '>' +'\n')
def __iadd__(self, text):
self.fp.write(text)
def __enter__(self):
self.fp = open(self.filename, "a+")
return self
def __exit__(self, exception_type, exception_value, traceback):
self.fp.close()
The idea is that i want to call the class and let it handle the with part for me

Why does one property work and the other not?

It happens that I have two property implementations. but one works and the other does not. That is, that in one it does not even enter the setters.
I gave myself the task of wandering online, and this happens to me is very rare, both are supposed to work.
Do you know why this happens?
Thank you
class QuickTasks():
def __init__(self, name=None, value=None):
self.name = name
self.value = value
#property
def name(self):
return self._name
#name.setter
def name(self, value):
if isinstance(value, str):
self._name = value
else:
raise TypeError("name must be str")
#property
def value(self):
return self._value
#value.setter
def value(self, value):
if isinstance(value, int):
self._value = value
else:
raise TypeError("value must be int")
obj = QuickTasks(name=4, value='j')
print(obj.name)
obj.name = 5
print(obj.name)
################################################################
class TreeNode(object):
def __init__(self, value = None):
self.value = value
self._left_node = None
self._right_node = None
#property
def value(self):
return self._value
#value.setter
def value(self, value):
if isinstance(value, int):
self._value = 8
else:
raise TypeError("value must be int")
def main():
tree_node = TreeNode(3)
#tree_node.value = 3
print (tree_node.value)
if __name__ == '__main__':
print("")
main()
When you hit the line:
obj = QuickTasks(name=4, value='j')
and it assigns to self.name in the QuickTasks initializer, it raises a TypeError, which you don't catch. That bypasses the rest of your code entirely, skipping all uses of the value property (and of TreeNode entirely).

How can I access methods of a class that is passed as argument to another class in python

I want to create a class(say, LockedAttributes) to access(READ/WRITE) some attributes safely by multiple threads.I want to pass those attributes that I want to share as a list to the LockedAttributes class. Some of the list elements are itself class objects with it's own setter and getter.
How can i access those setter/getter from a LockedAttribute class obj?
My use of getattr() setattr() might be wrong.
sample code:
class Coord:
def __init__(self, x=0.0, y=0.0, z=0.0):
self.x = x
self.y = y
self.z = z
def set_coordinator(self, x, y, z):
self.x = x
self.y = y
self.z = z
def get_coordinator(self):
return self.x, self.y, self.z
class LockedAttributes(object):
def __init__(self, obj):
self.__obj = obj
self.__lock = RLock()
def getmyPosition(self):
with self.__lock:
return self.__obj[0]
def getmySpeed(self):
with self.__lock:
return self.__obj[1]
def getcolPosition(self):
with self.__lock:
return self.__obj[2]
def getDistfromCol(self):
with self.__lock:
getattr(self, self.__obj[3])
def setDistfromCol(self, value):
with self.__lock:
setattr(self, self.__obj[3], value)
def getcolactivationFlag(self):
with self.__lock:
getattr(self, self.__obj[4])
def setcolactivationFlag(self, value):
with self.__lock:
setattr(self, self.__obj[3], value)
class OBU():
def __init__(self):
pos = Coord()
speed = Coord()
colpos = Coord()
distance_from_accident = 0.0
Flag = False
self.shared_attributes = LockedAttributes([ pos, speed, colpos, distance_from_accident, Flag])
mypos= self.shared_attributes.getmyPosition()
mypos.get_coordinator() # Not workinh
The __init__ method of the LockedAttributes class should take an argument so that you can actually pass a list object to it.
Change:
class LockedAttributes(object):
def __init__(self):
self.__obj = object
self.__lock = RLock()
To:
class LockedAttributes(object):
def __init__(self, obj):
self.__obj = obj
self.__lock = RLock()

Memoized objects still have their __init__() invoked?

So I am creating a memoized class, and have been observing a strange behavior.
Here's the snippet of the code:
class SomeClass(object):
_Memoized = {}
def __new__(cls, id: str, *args, **kwargs):
if id not in cls._Memoized:
print('New Instance')
cls._Memoized[id] = super(SomeClass, cls).__new__(cls, *args, **kwargs)
else:
print('Existing Instance')
return cls._Memoized[id]
def __init__(self, id: str):
print('Running init')
self.id = id
def test():
w1 = SomeClass(id='w1')
wx = SomeClass(id='w1')
print(id(w1), id(wx), id(w1) == id(wx))
test()
Running the above code results in:
New Instance
Running init
Existing Instance
Running init <===-------------------???
140008534476784 140008534476784 True
My questions: During the second invocation of SomeClass(), why does it execute the __init__ constructor? Wasn't the __init__ constructor only invoked on instantiating? Is there a way to prevent the __init__ from being invoked?
The purpose of __new__ is to create a new instance, which is why Python calls __init__ on it. You might instead override __call__ on a metaclass to avoid creating a new instance.
class MemoMeta(type):
def __init__(self, name, bases, namespace):
super().__init__(name, bases, namespace)
self.cache = {}
def __call__(self, id_, *args, **kwargs):
if id_ not in self.cache:
print('New Instance')
self.cache[id_] = super().__call__(id_, *args, **kwargs)
else:
print('Existing Instance')
return self.cache[id_]
class SomeClass(metaclass=MemoMeta):
def __init__(self, id_, *args, **kwargs):
print('Running init')
self.id = id_
def test():
w1 = SomeClass(id_='w1')
wx = SomeClass(id_='w1')
print(id(w1), id(wx), id(w1) == id(wx))
test()

Override abstract setter of property in Python 3

What is the simplest / most pythonic way to override only the setter of an abstract property in Python 3? Variant 3 seems to mean the least effort for the derived class implementor. Is it correct? Does it have disadvantages?
import abc
class A1(metaclass=abc.ABCMeta):
def __init__(self, x, **kwargs):
super().__init__(**kwargs)
self._x = x
#property
def x(self):
return self._x
#x.setter
#abc.abstractmethod
def x(self, value):
self._x = value
class B1(A1):
#property
def x(self):
return super().x
#x.setter
def x(self, value):
print("B1 setter")
super(B1, self.__class__).x.fset(self, value)
b1 = B1(x=1)
b1.x = 3
print(b1.x)
class A2(metaclass=abc.ABCMeta):
def __init__(self, x, **kwargs):
super().__init__(**kwargs)
self._x = x
#abc.abstractmethod
def _get_x(self):
return self._x
#abc.abstractmethod
def _set_x(self, value):
self._x = value
x = property(_get_x, _set_x)
class B2(A2):
def _get_x(self):
return super()._get_x()
def _set_x(self, value):
print("B2 setter")
super()._set_x(value)
x = property(_get_x, _set_x)
b2 = B2(x=1)
b2.x = 3
print(b2.x)
class A3(metaclass=abc.ABCMeta):
def __init__(self, x, **kwargs):
super().__init__(**kwargs)
self._x = x
def _get_x(self):
return self._x
#abc.abstractmethod
def _set_x(self, value):
self._x = value
x = property(
lambda self: self._get_x(),
lambda self, value: self._set_x(value))
class B3(A3):
def _set_x(self, value):
print("B3 setter")
super()._set_x(value)
b3 = B3(x=1)
b3.x = 3
print(b3.x)
So, yes, you listed a lot of ways in there - and although the one that requires more code is your variant 3, the most straighforard, least surprising way to do it is your variant 1 -
It just works, and is perfectly readable, no surprises - and there seems to be no simpler way than calling fget explicitly there.

Resources