I have a size-limited dictionary class, and I want to make the iter method works like this:
if the value not None, then generate. Otherwise, skip it.
Do you know how to implement it guys?
class myclass(object):
def __init__(self):
self.data = {1:'a', 2:None, 3:'c'}
def __iter__(self):
return iter(self.data.values())
def __next__(self): ## <== I THINK THIS IS A WRONG EXAMPLE
if iter(self): ## BUT I DON"T KNOW HOW TO FIX IT
return iter(self)
mc = myclass()
for i in mc:
print(i)
If your __iter__ method directly returns an iterator, you do not need to implement __next__; it will not be consulted in that case (it is the __next__ method of the returned iterator that is used instead).
Return a generator expression:
class myclass(object):
def __init__(self):
self.data = {1:'a', 2:None, 3:'c'}
def __iter__(self):
return (v for v in self.data.values() if v is not None)
Demo:
>>> class myclass(object):
... def __init__(self):
... self.data = {1:'a', 2:None, 3:'c'}
... def __iter__(self):
... return (v for v in self.data.values() if v is not None)
...
>>> list(myclass())
['a', 'c']
You'd have to return self from __iter__ if you wanted the class to be its own iterator; this would mean you'd need to track state between __next__ calls to know what value to return from each call. For your usecase, you most probably do not want that.
To create a single-use iterator:
class myclass(object):
def __init__(self):
self.data = {1:'a', 2:None, 3:'c'}
self.keys = self.data.keys()
self.index = 0
def __iter__(self):
return self
def __next__(self):
index = self.index
while 'searching for value':
if index >= len(self.keys):
raise StopIteration
key = self.keys[index]
value = self.data[key]
index += 1
if value is not None:
self.index = index
return value
As you can see, using Martjin's answer is much nicer.
Related
I would like to be able to unpack an object from a dict-similar class.
current:
f(**m.to_dict())
preferred
f(**m)
This would work if starstarprepare existed:
class M:
#... __getitem__, __setitem__
def __starstarprepare__(self):
md = self.to_dict()
return md
You can use collections.abc.Mapping.
from collections.abc import Mapping
class M(Mapping):
def __iter__(self):
return iter(self.to_dict())
def __getitem__(self, item):
return self.to_dict()[item]
def __len__(self):
return len(self.to_dict())
** works with any mapping type. One way to make M a mapping type is to subclass collections.abc.Mapping and implement __getitem__, __iter__, and __len__:
from collections.abc import Mapping
class M(Mapping):
def __init__(self):
self.a = 3
self.b = 5
def __getitem__(self, key):
return getattr(self, key)
def __iter__(self):
yield 'a'
yield 'b'
def __len__(self):
return 2
def foo(**kwargs):
for k, v in kwargs.items():
print(k, v)
m = M()
foo(**m)
If you already have a to_dict method, all three of the magic methods can be wrappers around the corresponding dict methods.
class M(Mapping):
def __init__(self):
self.a = 3
self.b = 5
def to_dict(self):
return {'a': self.a, 'b': self.b}
def __getitem__(self, key):
return self.to_dict()[key]
def __iter__(self):
return iter(self.to_dict())
def __len__(self):
return len(self.to_dict())
Solution due to #peter
class M:
# ... __getitem__ and other functions
def keys(self):
k = self.to_dict().keys()
return k
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.
So I want to call method on returned value from another method of same class.
class A():
def __init__(self,data):
self.data = data
def values(self,key):
return list(list(x.values())[0] for x in self.data['data'] if key in x.keys())
def length(self):
return len(self)
data ={"data":[{"country":"india"},{"state":"punjab"},{"country":"usa"}]}
obj = A(data)
res = obj.values('country') # returns ['india', 'usa']
res1 = obj.values('country').length() # returns AttributeError: 'list' object has no attribute 'length'
print(res)
print(res1)
i want both res and res1 to work.
I have also tried using #property decorator
class B():
def __init__(self,data):
self.data = data
def __call__(self, key):
self.key = key
return self
#property
def values(self):
self.valu = list(list(x.values())[0] for x in self.data['data'] if self.key in x.keys())
return self
def length(self):
return len(self.valu)
data ={"data":[{"country":"india"},{"state":"punjab"},{"country":"usa"}]}
obj = B(data)
res = obj('country').values.length() # returns 2
res1 = obj('country').values # returns <__main__.A object at 0x103a9fbe0>
print(res)
print(res1)
This way res works but res1 does not.
Thanks
.length() is not a python list function. Use len(listobject) instead.
Eg:
len(obj('country').values)
If you want to print the values. In your second example:
#Use
res1 = obj('country').data
#instead of
res1 = obj('country').values
Use below code to get list of countries
res1 = [_dict.get('country') for _dict in obj('country').data.get('data') if 'country' in _dict.keys()]
print(res1)
#['india', 'usa']
Final Updated working code:
class B():
def __init__(self,data):
self.data = data
def __call__(self, key):
self.key = key
return self
#property
def values(self):
self.valu = list(list(x.values())[0] for x in self.data['data'] if self.key in x.keys())
return self
def length(self):
return len(self.valu)
data ={"data":[{"country":"india"},{"state":"punjab"},{"country":"usa"}]}
obj = B(data)
res = obj('country').values.length() # returns 2
res1 = [_dict.get('country') for _dict in obj('country').data.get('data') if 'country' in _dict.keys()] # returns ['usa', 'india']
print(res)
print(res1)
I think the confusion here is on what object is the .length method invoked. In the first case, it is invoked on a list which does not have a .length method. In the second case, it is invoked on the B object which indeed has a .length method. The most straightforward and preferred solution is what #Subhrajyoti Das suggested.
If you still want your code (as described in the question) to work (or just want to know if this could actually be done), you could define a custom list object which would look like as follows:
class CustomList(list):
def length(self):
return len(self)
Now instead of making a list, you would make a CustomList.
Edit: Adding the complete example, as requested in the comment.
class CustomList(list):
def length(self):
return len(self)
class A():
def __init__(self,data):
self.data = data
def values(self,key):
return CustomList(CustomList(x.values())[0] for x in self.data['data'] if key in x.keys())
def length(self):
return len(self)
data ={"data":[{"country":"india"},{"state":"punjab"},{"country":"usa"}]}
obj = A(data)
res = obj.values('country') # returns ['india', 'usa']
res1 = obj.values('country').length() # 2
object has no attribute 'length'
print(res)
print(res1)
I am interested in counting the number of accesses to a dictionary's values. I am unsure how to include dictionary unpacking in the counter. Any tips?
from collections import defaultdict
class LDict(dict):
def __init__(self, *args, **kwargs):
'''
This is a read-counting dictionary
'''
super().__init__(*args, **kwargs)
self._lookup = defaultdict(lambda : 0)
def __getitem__(self, key):
retval = super().__getitem__(key)
self._lookup[key] += 1
return retval
def __setitem__(self, key, value):
super().__setitem__(key, value)
self._lookup[key] = self._lookup.default_factory()
def __delitem__(self, key):
super().__delitem__(self, key)
_ = self._lookup[key]
del self._lookup[key]
def list_unused(self):
return [key for key in self if self._lookup[key] == 0]
l = LDict(a='apple', b='bugger')
print({**l, **l})
print(l.list_unused())
_ = l['a']
print(l.list_unused())
You need to override more methods. Access is not centralized through __getitem__(): other methods like copy(), items(), etc. access the keys without going through __getitem()__. I would assume the ** operator uses items(), but you will need to handle ALL of the methods to keep track of EVERY access. In many cases you will have to make a judgement call. For example, does __repr__() count as an access? The returned string contains every key and value formatted, so I think it does.
I would recommend overriding all of these methods, because you have to do bookkeeping on assignment too.
def __repr__(self):
def __len__(self):
def __iter__(self):
def clear(self):
def copy(self):
def has_key(self, k):
def update(self, *args, **kwargs):
def keys(self):
def values(self):
def items(self):
EDIT: So apparently there's an important caveat here that directly relates to your implementation. if LDict extends dict, then none of these methods are invoked during the dictionary unpacking { **l, **l}.
Apparently you can follow the advice here though, and implement LDict without extending dict. This worked for me:
from collections import MutableMapping
class LDict(MutableMapping):
def __init__(self, *args, **kwargs):
'''
This is a read-counting dictionary
'''
self._lookup = defaultdict(lambda : 0)
self.data = {}
if kwargs:
self.data.update(kwargs)
def __getitem__(self, key):
retval = self.data[key]
self._lookup[key] += 1
return retval
def __setitem__(self, key, value):
self.data[key] = value
self._lookup[key] = self._lookup.default_factory()
def __delitem__(self, key):
del self.data[key]
_ = self._lookup[key]
del self._lookup[key]
def items(self):
print('items is being called!')
yield from self.data.items()
def __iter__(self):
print('__iter__ is being called!')
yield from self.data
def __len__(self):
return len(self.data)
def list_unused(self):
return [key for key in self if self._lookup[key] == 0]
l = LDict(a='apple', b='bugger')
print({**l, **l})
print(l.list_unused())
_ = l['a']
print(l.list_unused())
which produces the output:
__iter__ is being called!
__iter__ is being called!
{'b': 'bugger', 'a': 'apple'}
__iter__ is being called!
[]
__iter__ is being called!
[]
(I only implemented the bare minimum to get example to work, I still recommend implementing the set of methods I listed about if you want your counts to be correct!)
So I guess the answer to your question is you have to
Implement the __iter__(self) method
DO NOT inherit from dict().
As you know, python allows us simply override dict.__getitem__ method so we can do something different in there when someone tries to retrieve any value from it.
I want to do some code when one MyDict(dict) class instance is passed to update method of another python dict instance. See below:
class MyDict(dict):
def __getitem__(self, item):
print "Doing some stuff here"
return dict.__getitem__(self, item)
d1 = MyDict({'1': 1, '2': 2})
d2 = {}
# I want to have d1.__getitem__ called, but it does not work :-(
d2.update(d1)
Try using the collections.Mapping abstract base class (or collections.MutableMapping, if this is read-write).
import collections
class MyDict(collections.Mapping):
def __init__(self, *args, **kwargs):
self.data = dict(*args, **kwargs)
def __len__(self):
return len(self.data)
def __iter__(self):
return iter(self.data)
def __contains__(self, key):
return key in self.data
def __getitem__(self, key):
print 'Doing some stuff here'
return self.data[key]
All you need is to subclass MyDict from object and create .keys() method for it. See below:
class MyDict(object):
def __init__(self, items=()):
self._dict = dict(items)
def keys(self):
return self._dict.keys()
def __getitem__(self, item):
print "Doing some stuff for item:", item
return self._dict[item]
def __setitem__(self, item, value):
self._dict[item] = value
# You can add some more dict methods
d1 = MyDict({'1': 1, '2': 2})
d2 = {}
# Now you will see some stuff executed for each
# value extracted from d1 while updating d2
d2.update(d1)