Python is adding values to multiple instances - python

I have created a descriptor for lists.
After testing it seems that every time I append a value to a list of one instance, it is being added to another instance as well.
Even weirder, in the unittests it keeps appending to the list, and not resetting on every test.
My descriptor main class:
class Field(object):
def __init__(self, type_, name, value=None, required=False):
self.type = type_
self.name = "_" + name
self.required = required
self._value = value
def __get__(self, instance, owner):
return getattr(instance, self.name, self.value)
def __set__(self, instance, value):
raise NotImplementedError
def __delete__(self, instance):
raise AttributeError("Can't delete attribute")
#property
def value(self):
return self._value
#value.setter
def value(self, value):
self._value = value if value else self.type()
Descriptor list class:
class ListField(Field):
def __init__(self, name, value_type):
super(ListField, self).__init__(list, name, value=[])
self.value_type = value_type
def __set__(self, instance, value):
if not isinstance(value, list):
raise TypeError("{} must be a list".format(self.name))
setattr(instance, self.name, value)
def __iter__(self):
for item in self.value:
yield item
def __len__(self):
return len(self.value)
def __getitem__(self, item):
return self.value[item]
def append(self, value):
if not isinstance(value, self.value_type):
raise TypeError("Value is list {} must be of type {}".format(self.name, self.value_type))
self.value.append(value)
Unittests:
# Class I created solely for testing purposes
class ListTestClass(object):
l = ListField("l", int)
class TestListFieldClass(unittest.TestCase):
def setUp(self):
self.listobject = ListTestClass()
def test_add(self):
# The first number is added to the list
self.listobject.l.append(2)
def test_multiple_instances(self):
# This test works just fine
l1 = ListField("l1", int)
l2 = ListField("l2", int)
l1.append(1)
l2.append(2)
self.assertEqual(l1[0], 1)
self.assertEqual(l2[0], 2)
def test_add_multiple(self):
# This test works just fine
l1 = ListField("l1", int)
l1.append(1)
l1.append(2)
self.assertEqual(l1[0], 1)
self.assertEqual(l1[1], 2)
def test_add_error(self):
# This test works just fine
with self.assertRaises(TypeError):
l1 = ListField("l1", int)
l1.append("1")
def test_overwrite_list(self):
# This test works just fine
l1 = ListField("l1", int)
l1 = []
l1.append(1)
def test_overwrite_error(self):
# This test works just fine
l1 = ListTestClass()
l1.l.append(1)
with self.assertRaises(TypeError):
l1.l = "foo"
def test_multiple_model_instances(self):
# I create 2 more instances of ListTestClass
l1 = ListTestClass()
l2 = ListTestClass()
l1.l.append(1)
l2.l.append(2)
self.assertEqual(l1.l[0], 1)
self.assertEqual(l2.l[0], 2)
The last test fails
Failure
Traceback (most recent call last):
File "/home/user/project/tests/test_fields.py", line 211, in test_multiple_model_instances
self.assertEqual(l1.l[0], 1)
AssertionError: 2 != 1
When I look at the values for l1.1 and l2.l, they both have a list containing [2, 1, 2]
What am I missing here?
I looked to the memory addresses and it seems that the lists all point to the same object.
class ListFieldTest(object):
lf1 = ListField("lf1", int)
class TestClass(object):
def __init__(self):
l1 = ListFieldTest()
l2 = ListFieldTest()
l1.lf1.append(1)
l2.lf1.append(2)
print(l1.lf1)
print(l2.lf1)
print(hex(id(l1)))
print(hex(id(l2)))
print(hex(id(l1.lf1)))
print(hex(id(l2.lf1)))
This prints
[1, 2]
[1, 2]
0x7f987da018d0 --> Address for l1
0x7f987da01910 --> Address for l2
0x7f987d9c4bd8 --> Address for l1.lf1
0x7f987d9c4bd8 --> Address for l2.lf1

ListTestClass.l is a class attribute, so it is shared by all instances of the class. Instead, you should create an instance attribute, eg in the __init__ method:
class ListTestClass(object):
def __init__(self):
self.l = ListField("l", int)
Similar remarks apply to ListFieldTest. There may be other similar problems elsewhere in your code, I haven't examined it closely.

According to this source, the proper form is
class ListTestClass(object):
l_attrib = ListField("l", int)
def __init__(self)
self.l = l_attrib

Thanks to both #PM 2Ring and volcano I found the answer.
In the end this works great for value types:
class IntTestClass(object):
i = IntegerField("i")
However for a reference type (like a list) that won't work and you have to add a new list
class ListTestClass(object):
l = ListField("l", int)
def __init__(self):
self.l = []

Related

Is it possible to create a list where types in it cannot be changed, but the value itself can?

I'm wondering if it exists a way to create a list where variables inside could be changed to other variables but exclusively if they are of the same type.
for instance
a=[0, 1.0, 'blabla']
a[0] = 0 # is possible
a[1] = 2. # is possible
a[2] = 'albalb' # is possible
a[0] = 1.2 # is not possible
a[1] = 'no' # is not possible
a[2] = 1 # is not possible
I cannot use tuple to do that because it is immutable.
My goal is to create a list where the number of value in it can vary, so append, insert and pop will be useful. I also want slicing available in order to select a part of the list.
At the end, the list will contain my own classes which describe neuronal models. I have different models possible, so different classes.
With the list, I would like to do what we can do with Lists but I don't want the type of a variable in the list to change, except if I insert a neuron at a position. In that case, every variable after that position is shifted too the right.
for instance:
class A():
def __init__(self):
self.A = 0
class B():
def __init__(self):
self.A = 1
class C():
def __init__(self):
self.A = 2
class D():
def __init__(self):
self.A = 3
MyList = [A(),B(),C()]
print([M.A for M in MyList])
#insert
MyList.insert(1,D())
print([M.A for M in MyList])
#slicing
MyList2 = MyList[1:3]
print([M.A for M in MyList2])
#replace if the variable is the same type that the variable of the list to replace
MyList[0] = A()
print([M.A for M in MyList])
#So this should not be possible
MyList[0] = B()
print([M.A for M in MyList])
I would like something really close from the List object, so I expected that it could already exist.
Solution 1 Wrap all methods which modify the list in-place and override __setitem__()
class RewritingLockedTypeList(list):
def __init__(self, original_list):
super().__init__(original_list)
self.types = [type(n) for n in original_list]
def __setitem__(self, key, value):
if self.types[key] != type(value):
raise TypeError(f"Value at index {key} should be {self.types[key]}!")
super().__setitem__(key, value)
def wrap_method(method_name):
orig_method = getattr(RewritingLockedTypeList, method_name)
def new_method(self, *args, **kwargs):
result = orig_method(self, *args, **kwargs)
self.types = [type(n) for n in self]
return result
setattr(RewritingLockedTypeList, method_name, new_method)
for method in ["append", "clear", "extend", "insert", "pop", "remove", "reverse", "sort"]:
wrap_method(method)
Solution 2 Override all methods which modify the list in-place and override __setitem__() too
class LockedTypeList(list):
def __init__(self, original_list):
super().__init__(original_list)
self.types = [type(n) for n in original_list]
def __setitem__(self, key, value):
if self.types[key] != type(value):
raise TypeError(f"Value at index {key} should be {self.types[key]}!")
super().__setitem__(key, value)
def __delitem__(self, key):
del self.types[key]
super().__delitem__(key)
def append(self, thing):
self.types.append(type(thing))
super().append(thing)
def clear(self):
self.types.clear()
super().clear()
def extend(self, objects):
self.types.extend(type(o) for o in objects)
super().extend(objects)
def insert(self, idx, obj):
self.types.insert(idx, type(obj))
super().insert(idx, obj)
def pop(self, index=0):
self.types.pop(index)
super().pop(index)
def remove(self, value):
idx = self.index(value)
self.pop(idx)
def reverse(self):
self.types.reverse()
super().reverse()
def sort(self, key=lambda n: n, reverse=False):
super().sort(key=key, reverse=reverse)
self.types = [type(n) for n in self]
The second solution is longer, but faster for long lists.
Usage
a=LockedTypeList([0, 1.0, 'blabla'])
But you maybe should think about using a class and properties with type checking instead of this ugly list.

understanding a python descriptors example (TypedProperty)

Here is a slightly modified version of some code found in a python book:
class TypedProperty(object):
def __init__(self,name,type,default=None):
self.name = "_" + name
self.type = type
self.default = default if default else type()
def __get__(self,instance,cls):
return getattr(instance,self.name,self.default)
def __set__(self,instance,value):
if not isinstance(value,self.type):
raise TypeError("Must be a %s" % self.type)
setattr(instance,self.name,value)
class Foo(object):
name = TypedProperty("name",str)
num = TypedProperty("num",int,42)
f = Foo()
f.name = 'blah'
My question: why are we creating attributes in f? In the code above, TypedProperty is written such that f.name = 'blah' creates the attribute "_name" in the instance f.
Why not save the values as attributes of the class TypedProperty? Here is what I had in mind:
class TypedProperty2(object):
def __init__(self, val, typ):
if not isinstance(val, typ):
raise TypeError()
self.value = val
self.typ = typ
def __get__(self, instance, owner):
return self.value
def __set__(self, instance, val):
if not isinstance(val, self.typ):
raise TypeError()
self.value = val
Is this an arbitrary design decision?
All instances of the class will share the same instance of the descriptor (e.g. TypedProperty). So, if you store the value on the TypedProperty, then all instances of Foo will have the same value for the name and num values. This is usually not desirable (or expected) for descriptors.
e.g. if you run the following script:
class TypedProperty2(object):
def __init__(self, val, typ):
if not isinstance(val, typ):
raise TypeError()
self.value = val
self.typ = typ
def __get__(self, instance, owner):
return self.value
def __set__(self, instance, val):
if not isinstance(val, self.typ):
raise TypeError()
self.value = val
class Foo(object):
name = TypedProperty2("name", str)
f1 = Foo()
f1.name = 'blah'
f2 = Foo()
print(f2.name)
f2.name = 'bar'
print(f1.name)
You'll see the following output:
blah
bar
so we can see that initially f2 had f1's name and then, after changing the name of f2, f1 picked up f2's name.

Using __setattr__ + __slots__ in a python3 class

I am trying to be all fancy with sub element attribute access in a custom class hierarchy.
My fanciness works in that I can successfully use descriptors to do this.
I want to be even more fancy and make the class RefHolder (shown below in the testcase) use slots to save space.
When I try to use slots though, I get RuntimeError: maximum recursion depth exceeded
Note that I have already tried looking at existing solutions for this, the most closely matching I could find being this one:
https://stackoverflow.com/a/19566973/1671693
I have tried this in the testcase below but I am still get the runtimeerror.
Note that in the testcase, if the commented lines are used instead of the ones directly beneath them and __slots__ is removed from RefHolder,
The testcase passes.
Any suggestions?
Additionally, I am creating an object for every attribute access which seems expensive, are there any suggestions on a more efficient way of achieving the same behavior? Thanks!
import unittest
class RefHolder():
__slots__ = ['__obj', 'get_value']
def __init__(self, obj, get_value=False):
self.__dict__['__obj'] = obj
self.__dict__['get_value']=get_value
def get_sub(self, name):
#attr = self.__dict__['__obj'].find_by_name(name)
attr = self.__dict__['__obj'].__get__(self, RefHolder).find_by_name(name)
if attr is None:
raise AttributeError("Can't find field {}".format(name))
return attr
def __getattr__(self, name):
attr = self.get_sub(name)
#if self.__dict__['get_value']:
if self.__dict__['get_value'].__get__(self, RefHolder):
return attr.Value
else:
return attr
def __setattr__(self, name, value):
attr = self.get_sub(name)
#if self.__dict__['get_value']:
if self.__dict__['get_value'].__get__(self, RefHolder):
attr.Value = value
else:
raise AttributeError("{} is read only in this context".format(name))
class ContainerAccess():
__slots__ = ['get_value']
def __init__(self, get_value=False):
self.get_value = get_value
def __get__(self, obj, objtype=None):
if obj is None:
return self
return RefHolder(obj, self.get_value)
def __set__(self, obj, value):
raise AttributeError("Read Only attribute".format(value))
class PropVal():
def __init__(self, val):
self.Value = val
#property
def Value(self):
return self._value
#Value.setter
def Value(self, value):
self._value = value
class T():
get = ContainerAccess()
getv = ContainerAccess(get_value=True)
def __init__(self):
self.store = {}
self._value = 0
def find_by_name(self, name):
return self.store.get(name)
class T2(T):
pass
class TestDesc(unittest.TestCase):
def test_it(self):
t = T()
t2 = T2()
t.store['my_val'] = PropVal(5)
t.store['my_val2'] = PropVal(6)
t2.store['my_val'] = PropVal(1)
self.assertEqual(t.get.my_val.Value, 5)
self.assertEqual(t.get.my_val2.Value, 6)
self.assertEqual(t2.get.my_val.Value, 1)
t.get.my_val.Value = 6
self.assertEqual(t.get.my_val.Value, 6)
with self.assertRaises(AttributeError):
t.get.blah.Value = 6
#self.assertEqual(t.get.my_other_val.Value, None)
self.assertEqual(t.getv.my_val, 6)
t.getv.my_val = 7
self.assertEqual(t.getv.my_val, 7)
with self.assertRaises(AttributeError):
t.get.my_val = 7

Pythonic alias for instance variable?

I have a class in which I store data in a list for inheritance reasons. I would like to know, and I have done my share of googling, is there a cleaner way other than creating getter/setter functions and properties to give a alias to the element in this list?
For example...
class Serializable(object):
"""Adds serialization to from binary string"""
def encode(self):
"""Pack into struct"""
return self.encoder.pack(*self)
def decode(self, data_str):
"""Unpack from struct"""
self.data = self.encoder.unpack(data_str)
return self.data
class Ping(Serializable):
encoder = Struct("!16sBBBL")
def __init__(self, ident=create_id(), ttl=TTL, hops=0, length=0):
self.data = [ident, 1, ttl, hops, length]
self.ident = property(self.data[0])
def __getitem__(self, index):
return self.data[index]
#property
def ident(self):
return self.data[0]
#ident.setter
def ident(self, value):
self.data[0] = value
#property
def protocol(self):
return self.data[1]
#protocol.setter
def protocol(self, protocol):
self.data[1]
I would prefer a more compact solution to reference object.ident while maintaining the ability to pack and unpack as above.
If you store your values/properties in a dictionary instead:
def __init__(self, ident=create_id(), ttl=TTL, hops=0, length=0):
self.data = {
'ident': ident,
'protocol': 1,
'ttl': hops,
'length': length,
}
And then override __getattr__ and __setattr__:
def __getattr__(self, attr):
return self.data[attr]
def __setattr__(self, attr, value):
if attr == 'data':
object.__setattr__(self, attr, value)
else:
self.data[attr] = value
Now you can do this:
>>> ping = Ping()
>>> ping.protocol
1
>>> ping.protocol = 2
>>> ping.protocol
2
If self.data absolutely has to be a list, you can do this instead:
class Ping(Serializable):
mapping = ('ident', 'protocol', 'ttl', 'hops', 'length')
encoder = Struct("!16sBBBL")
def __init__(self, ident=create_id(), ttl=TTL, hops=0, length=0):
self.data = [ident, 1, ttl, hops, length]
def __getitem__(self, index):
return self.data[index]
def __getattr__(self, attr):
index = self.mapping.index(attr)
return self.data[index]
def __setattr__(self, attr, value):
if attr == 'data':
object.__setattr__(self, attr, value)
else:
index = self.mapping.index(attr)
self.data[index] = value
def alias_property(key):
return property(
lambda self: getattr(self, key),
lambda self, val: setattr(self, key, val),
lambda self: delattr(self, key))
class A(object):
def __init__(self, prop):
self.prop = prop
prop_alias = alias_property('prop')
If your problem is just shorten the code to access ident, you may just use "property" in the "old style" - that is, you pass to it, as parameters, the getter and setter functions, instead of using it as a decorator.
In this case, the functions are so small, they can be lambda functions, without affecting code readbility.
class Ping(Serializable):
encoder = Struct("!16sBBBL")
def __init__(self, ident=None, ttl=TTL, hops=0, length=0):
if ident is None:
ident = create_id()
self.data = [ident, 1, ttl, hops, length]
# The line bellow looks like garbage -
# it does not even make sense as a call to `property`
# should have a callable as first parameter
# returns an object that is designed to work as a class attribute
# self.ident = property(self.data[0])
# rather:
self.ident = ident
# this will use the property defined bellow
def __getitem__(self, index):
return self.data[index]
ident = property(lambda s: s.data[0], lambda s, v: s.data[0].__setitem__(0, v)
protocol = property(lambda s: s.data[1], lambda s, v: s.data[1].__setitem__(1, v)

How do I create a tuple with an attribute using Python?

I have a class WeightedArc defined as follows:
class Arc(tuple):
#property
def tail(self):
return self[0]
#property
def head(self):
return self[1]
#property
def inverted(self):
return Arc((self.head, self.tail))
def __eq__(self, other):
return self.head == other.head and self.tail == other.tail
class WeightedArc(Arc):
def __new__(cls, arc, weight):
self.weight = weight
return super(Arc, cls).__new__(arc)
This code clearly doesn't work, because self isn't defined for WeightArc.__new__. How do I assign the attribute weight to the WeightArc class?
The fixed-up version of your original code is:
class WeightedArc(Arc):
def __new__(cls, arc, weight):
self = tuple.__new__(cls, arc)
self.weight = weight
return self
Another approach to look at the verbose option for collections.namedtuple to see an example of how to subclass tuple:
>>> from collections import namedtuple, OrderedDict
>>> _property = property
>>> from operator import itemgetter as _itemgetter
>>> Arc = namedtuple('Arc', ['head', 'tail'], verbose=True)
class Arc(tuple):
'Arc(head, tail)'
__slots__ = ()
_fields = ('head', 'tail')
def __new__(_cls, head, tail):
'Create new instance of Arc(head, tail)'
return _tuple.__new__(_cls, (head, tail))
#classmethod
def _make(cls, iterable, new=tuple.__new__, len=len):
'Make a new Arc object from a sequence or iterable'
result = new(cls, iterable)
if len(result) != 2:
raise TypeError('Expected 2 arguments, got %d' % len(result))
return result
def __repr__(self):
'Return a nicely formatted representation string'
return 'Arc(head=%r, tail=%r)' % self
def _asdict(self):
'Return a new OrderedDict which maps field names to their values'
return OrderedDict(zip(self._fields, self))
def _replace(_self, **kwds):
'Return a new Arc object replacing specified fields with new values'
result = _self._make(map(kwds.pop, ('head', 'tail'), _self))
if kwds:
raise ValueError('Got unexpected field names: %r' % kwds.keys())
return result
def __getnewargs__(self):
'Return self as a plain tuple. Used by copy and pickle.'
return tuple(self)
head = _property(_itemgetter(0), doc='Alias for field number 0')
tail = _property(_itemgetter(1), doc='Alias for field number 1')
You can cut, paste, and modify this code, or just subclass from it as shown in the namedtuple docs.
To extend this class, build off of the fields in Arc:
WeightedArc = namedtuple('WeightedArc', Arc._fields + ('weight',))
Another approach to look at the verbose option for collections.namedtuple to see an example of how to subclass tuple
Better yet, why not use namedtuple ourselves? :)
class Arc(object):
def inverted(self):
d = self._asdict()
d['head'], d['tail'] = d['tail'], d['head']
return self.__class__(**d)
class SimpleArc(Arc, namedtuple("SimpleArc", "head tail")): pass
class WeightedArc(Arc, namedtuple("WeightedArc", "head tail weight")): pass

Categories