python lost data-descriptor as a instance attribute? - python-3.x

class data_attr_set_pass(object):
def __init__(self, inner=None, name=""):
self.inner = inner
self.name = name
def __get__(self, instance, owner):
return self.inner
def __set__(self, instance, value):
pass
def __repr__(self):
return str(self.name) + ":" + str(self.inner)
class data_attr(object):
def __init__(self, inner=None, name=""):
self.inner = inner
self.name = name
def __get__(self, instance, owner):
return self.inner
def __set__(self, instance, value):
self.inner = value
def __repr__(self):
return str(self.name) + ":" + str(self.inner)
class non_data_attr(object):
def __init__(self, inner=None, name=""):
self.inner = inner
self.name = name
def __get__(self, instance, owner):
return self.inner
def __repr__(self):
return str(self.name) + ":" + str(self.inner)
class Myclass(object):
x = data_attr_set_pass(11, "class attr")
def __init__(self):
self.x = data_attr(890, "instance attr")
print(Myclass.x)
m = Myclass()
print(Myclass.x)
print(m.x)
print(Myclass.__dict__)
print(m.__dict__)
output:
11
11
11
{'__module__': '__main__', 'x': class attr:11, '__init__': <function Myclass.__init__ at 0x7f0dcc2d1378>, '__dict__': <attribute '__dict__' of 'Myclass' objects>, '__weakref__': <attribute '__weakref__' of 'Myclass' objects>, '__doc__': None}
{}
I had some tests for usage of python data-descriptor. And one test had very strange behaviour. I know that descriptor should be class attr, it's just a test:).
I put pass in __set__ of class data_attr_set_pass. So as far as I know, self.x = data_attr(890, "instance attr") in __init__ will put data_attr(890, "instance attr") into self.__dict__. But it can be found nowhere.
why does it happend and where can I find the data-descriptor?
==================================================================
It's really my misunderstanding, I just run first test in code below in which I put 2 into self.__dict__["x"] directly, the second one can prove it.
class Myclass(object):
x = data_attr_set_pass(11, "class attr")
def __init__(self):
self.__dict__["x"] = 2
print(Myclass.x)
m = Myclass()
print(Myclass.x)
print(m.x)
print(Myclass.__dict__)
print(m.__dict__)
print('================')
class Myclass(object):
x = data_attr_set_pass(1324, "class attr")
def __init__(self):
self.x = 2232
print(Myclass.x)
m = Myclass()
print(Myclass.x)
print(m.x)
print(Myclass.__dict__)
print(m.__dict__)

You say
I put pass in __set__ of class data_attr_set_pass. So as far as I know, self.x = data_attr(890, "instance attr") in __init__ will put data_attr(890, "instance attr") into self.__dict__.
But why would it do that? You didn't write data_attr_set_pass.__set__ to insert the value into the instance __dict__. You wrote your __set__ to do nothing but pass.
Assigning something to self.x does exactly what you wrote your __set__ to do: nothing.

Related

Question about setter and calling MyClass()

I have a class like this:
class A:
b = ""
#property
some_property(self):
raise Error()
#some_property.setter
def some_property(self, some_property_var)
self.b = some_property_var
and i want to do A(some_property='A'), but i can't because
TypeError: A() takes no arguments
. Is there any way to make it work?
I think this is a better example of how to demonstrate what you are trying to do;
#set a property in a class in python
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
#property
def name(self):
return self._name
#name.setter
def name(self, name):
self._name = name
#property
def age(self):
return self._age
#age.setter
def age(self, age):
self._age = age
p = Person("John", 36)
So in your case;
class A:
def __init__(self, b):
self.b = b
#property
def b(self):
return self._b
#b.setter
def b(self, b):
self._b = b
a = A(1)
print(a.b)
Based on your comments;
#create a class that takes keyword arguments
class A:
def __init__(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
a = A(a=1, b=2)

Python: detect changes in a subclass

Let's consider the following example:
class SubClass:
def __init__(self):
self._param = None
#property
def param(self):
print('read param')
return self._param
#param.setter
def param(self, value):
print('set param')
self._param = value
class MainClass:
def __init__(self):
self._var = SubClass()
#property
def var(self):
print('read var')
return self._var
#var.setter
def var(self, value):
print('set var')
self._var = value
If I do:
cls = MainClass()
cls.var.param = 3
I obtain:
'read var'
'set param'
How can I make MainClass aware that var.param has changed?
Useful additional info: consider that in my actual code param is not a scalar but an array with hundreds of elements, so I would like to avoid to create a copy and then just compare them. Moreover, param and var are not the only properties.
One approach is to pass a method from MainClass as an argument when instantiating SubClass, and have SubClass call it whenever it changes a variable:
class SubClass:
def __init__(self, changed):
self._param = None
self.changed = changed
#property
def param(self):
print('read param')
return self._param
#param.setter
def param(self, value):
print('set param')
self._param = value
self.changed('param')
class MainClass:
def __init__(self):
self._var = SubClass(self.changed)
def changed(self, name):
print("Subclass changed: ", name, getattr(self._var, name))
#property
def var(self):
print('read var')
return self._var
#var.setter
def var(self, value):
print('set var')
self._var = value
m = MainClass()
m.var.param = 'test'

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()

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