'MyList' object does not support item assignment - python

Dears,
I am trying to understand better the OOP paradigm in Python, so I've created this simple class
class MyList:
def __init__(self,list):
self.list=list
list1=MyList([1,2,3,4,5])
Until here is everything fine, the problem occurs when I try to set a value to any element of my list. As example:
list1[0]=5
Then I got this TypeError: 'MyList' object does not support item assignment
Someone could help me with this?

You don't have any code in the class that allows for item assignment. For an object to allow item assignment, it needs to implement __setitem__.
You would need something like:
class MyList:
def __init__(self,list):
self.list=list
def __setitem__(self, i, elem):
self.list[i] = elem

Since you're trying to better understand OOP, here's something you can try: Subclass list itself, so your class gets all the properties that lists do.
class MyList(list):
def __init__(self,list):
super().__init__(list)
list1 = MyList([1,2,3,4,5])
list1[0] = 5
print(list1)
# [5, 2, 3, 4, 5]

class MyList:
def __init__(self,list):
self.list=list
def __str__(self):
return str(self.__dict__)
list1=MyList([1,2,3,4,5])
print(list1)
list1.list[0]=5
print(list1)
You need to assign to the list attribute not to the class

You are probably trying to create a new MyList type which should have the functionalities of a normal Python list plus some functionalities you might like to add or modify. For this to work, you need to inherit from the inbuilt Python list class, like so,
class MyList(list):
pass
list1=MyList([1,2,3,4,5])
list1[0]=5
Now in your MyList class, define only those methods which you want to change in the inbuilt list class

I got it!
I implemented the special method setitem and getitem and it worked. Thank you all
class MyList:
def __init__(self,list):
self.list=list
def __getitem__(self, index):
return self.list[index]
def __setitem__(self, index, value):
self.list[index] = value

Related

Some doubts about #property in python 3

In order not to extend myself too much I will give a basic and hypothetical example of what I am trying to do.
Suppose the following class:
class foo():
def __init__(self):
self.keywords = []
## this method returns the entire list
def get_keywords(self):
return self.keywords
def set_keywords(self, value):
self.keywords.append(value)
But I want to code this in a pythonic way using the #property decorator.
My (wrong) attempt to do this:
class foo:
def __init__(self):
self.key = []
#property
def key(self):
return self.__key
#key.setter
def key(self, value):
self.__key.append(value)
So, whats is wrong in my attempt ?
ps: English is not my native language and I hope my doubt is understandable.
In your original code, self.set_keywords only appends to an existing list; it does not let you initialize the value of keywords to an arbitrary list. This restriction is preserved in your property-based code, which means you cannot assign directly to self.key; you have to initialize the underlying list in __init__ directly.
class foo:
def __init__(self):
# self.key = [] is equivalent to `self.__key.append([])`, but
# self.__key doesn't exist yet. (And would be wrong even if it did.)
self.__key = []
#property
def key(self):
return self.__key
#key.setter
def key(self, value):
self.__key.append(value)
However, this means an assignment like self.key = 3 doesn't actually perform what most people would expect of an assignment. It doesn't overwrite the old value, it adds to it instead. Use the setter to provide a fixed list, but a different method to add to an existing one.
class foo:
def __init__(self):
self.__keys = []
#property
def keys(self):
return self.__keys
#keys.setter
def keys(self, values):
self.__keys = values
def add_key(self, value):
self.__key.append(value)
And finally, it's not necessarily more Pythonic to use a property if you don't actually do any sort of extra work or validation in the getter or setter. If all you are doing is wrapping access to an underlying value, just let the value be used directly.
class foo:
def __init__(self):
self.keys = []
self.keys = [1,2,3]
print(self.keys)
self.keys.append(4)
# etc
The nice thing about properties is that if you start by allowing direct access to keys, then nothing about how you use keys changes if you later decide to replace it with a property.
You can give this a try:
class Foo:
def __init__(self):
self._key = []
#property
def key(self):
return self._key
#key.setter
def key(self, value):
self._key = value
Here are my two cents:
Rename the class foo to Foo
You can't initialize self.key, as this is the property, so initialize the correct variable in the constructor (i.e. __init__)
Private vars are prefixed with one _ scope and not two (two __ are Python internals)
I suppose you rather want my_instance.key = ['spam', 'eggs'] to replace the foo._key value than extend it. Because this is kind of a "setter" and that would result in a weird behaviour, or at least another developer won't expect that behaviour from that setter/function
However, and that's important: As long as you're only doing this, you won't need properties. You can simply initialize self.keys in the constructor and froget about the property and setter function. Later on, when you want to change the behaviour, you can still add the property and setter. That's one reason why we've properties in Python, so that you won't have to refactor your whole code in case "a bit more logic" comes into place.
Btw. if you're really depending everything on those dict functions, you might also want to inherit your class from the dict class. Depends what you're up to.

Iterator for custom class in Python 3

I'm trying to port a custom class from Python 2 to Python 3. I can't find the right syntax to port the iterator for the class. Here is a MVCE of the real class and my attempts to solve this so far:
Working Python 2 code:
class Temp:
def __init__(self):
self.d = dict()
def __iter__(self):
return self.d.iteritems()
temp = Temp()
for thing in temp:
print(thing)
In the above code iteritems() breaks in Python 3. According to this highly voted answer, "dict.items now does the thing dict.iteritems did in python 2". So I tried that next:
class Temp:
def __init__(self):
self.d = dict()
def __iter__(self):
return self.d.items()
The above code yields "TypeError: iter() returned non-iterator of type 'dict_items'"
According to this answer, Python 3 requires iterable objects to provide a next() method in addition to the iter method. Well, a dictionary is also iterable, so in my use case I should be able to just pass dictionary's next and iter methods, right?
class Temp:
def __init__(self):
self.d = dict()
def __iter__(self):
return self.d.__iter__
def next(self):
return self.d.next
This time it's giving me "TypeError: iter() returned non-iterator of type 'method-wrapper'".
What am I missing here?
As the error message suggests, your __iter__ function does not return an iterator, which you can easily fix using the built-in iter function
class Temp:
def __init__(self):
self.d = {}
def __iter__(self):
return iter(self.d.items())
This will make your class iterable.
Alternatively, you may write a generator yourself, like so:
def __iter__(self):
for key,item in self.d.items():
yield key,item
If you want to be able to iterate over keys and items separately, i.e. in the form that the usual python3 dictionary can, you can provide additional functions, for example
class Temp:
def __init__(self, dic):
self.d = dic
def __iter__(self):
return iter(self.d)
def keys(self):
return self.d.keys()
def items(self):
return self.d.items()
def values(self):
return self.d.values()
I'm guessing from the way you phrased it that you don't actually want the next() method to be implemented if not needed. If you would, you would have to somehow turn your whole class into an iterator and somehow keep track of where you are momentarily in this iterator, because dictionaries themselves are not iterators. See also this answer.
I don't know what works in Python 2. But on Python 3 iterators can be most easily created using something called a generator. I am providing the name and the link so that you can research further.
class Temp:
def __init__(self):
self.d = {}
def __iter__(self):
for thing in self.d.items():
yield thing

Python Class design: attributes

I constructed a class:
class Foo (object):
def __init__(self,List):
self.List=List
#property
def numbers(self):
L=[]
for i in self.List:
if i.isdigit():
L.append(i)
return L
#property
def letters(self):
L=[]
for i in self.List:
if i.isalpha():
L.append(i)
return L
>>> inst=Foo(['12','ae','45','bb'])
>>> inst.letters
['ae', 'bb']
>>> inst.numbers
['12', '45']
How can I add attributes so I could do inst.numbers.odd that would return ['45']?
Your numbers property returns a list, so a numbers.odd won't work.
However, you could follow a workflow like:
define a small class Numbers, that would define two properties even and odd
For example, Numbers could take a list as argument of its __init__, the even property would return only the even number of this list [i for i in List if int(i)%2 == 0] (and odd the odd ones)...
create an instance of Numbers in your Foo.numbers property (using your Foo.List to initialize it) and return this instance...
Your Numbers class could directly subclass the builtin list class, as suggested. You could also define it like
class Numbers(object):
def __init__(self,L):
self.L = L
#property
def even(self):
return [i for i in self.L if not int(i)%2]
def __repr__(self):
return repr(self.L)
Here, we returning the representation of Numbers as the representation of its L attribute (a list). Fine and dandy until you want to append something to a Numbers instance, for example: you would have to define a Numb.append method... It might be easier to stick with making Numbers a subclass of list:
class Numbers(list):
#property
def even(self):
...
Edited: corrected the // by a %, because I went too fast and wasn't careful enough
Here's a silly example:
class mylst(list):
#property
def odd(self):
return [ i for i in self if int(i)%2 == 1 ]
class Foo(object):
def __init__(self,lst):
self.lst = list(lst)
#property
def numbers(self):
return mylst( i for i in self.lst if i.isdigit() )
a = Foo(["1","2","3","ab","cd"])
print(a.numbers)
print(a.numbers.odd)
Basically, we just subclass list and add a property odd which returns another list. Since our structure is a subclass of list, it is virtually indistinguishable from the real thing (Horray duck typing!). mylst.odd could even return a new instance of mylst if you wanted to be able to filter it again (e.g. a.numbers.odd.in_fibinocci )

A python class that acts like dict

I want to write a custom class that behaves like dict - so, I am inheriting from dict.
My question, though, is: Do I need to create a private dict member in my __init__() method?. I don't see the point of this, since I already have the dict behavior if I simply inherit from dict.
Can anyone point out why most of the inheritance snippets look like the one below?
class CustomDictOne(dict):
def __init__(self):
self._mydict = {}
# other methods follow
Instead of the simpler...
class CustomDictTwo(dict):
def __init__(self):
# initialize my other stuff here ...
# other methods follow
Actually, I think I suspect the answer to the question is so that users cannot directly access your dictionary (i.e. they have to use the access methods that you have provided).
However, what about the array access operator []? How would one implement that? So far, I have not seen an example that shows how to override the [] operator.
So if a [] access function is not provided in the custom class, the inherited base methods will be operating on a different dictionary?
I tried the following snippet to test out my understanding of Python inheritance:
class myDict(dict):
def __init__(self):
self._dict = {}
def add(self, id, val):
self._dict[id] = val
md = myDict()
md.add('id', 123)
print md[id]
I got the following error:
KeyError: < built-in function id>
What is wrong with the code above?
How do I correct the class myDict so that I can write code like this?
md = myDict()
md['id'] = 123
[Edit]
I have edited the code sample above to get rid of the silly error I made before I dashed away from my desk. It was a typo (I should have spotted it from the error message).
class Mapping(dict):
def __setitem__(self, key, item):
self.__dict__[key] = item
def __getitem__(self, key):
return self.__dict__[key]
def __repr__(self):
return repr(self.__dict__)
def __len__(self):
return len(self.__dict__)
def __delitem__(self, key):
del self.__dict__[key]
def clear(self):
return self.__dict__.clear()
def copy(self):
return self.__dict__.copy()
def has_key(self, k):
return k in self.__dict__
def update(self, *args, **kwargs):
return self.__dict__.update(*args, **kwargs)
def keys(self):
return self.__dict__.keys()
def values(self):
return self.__dict__.values()
def items(self):
return self.__dict__.items()
def pop(self, *args):
return self.__dict__.pop(*args)
def __cmp__(self, dict_):
return self.__cmp__(self.__dict__, dict_)
def __contains__(self, item):
return item in self.__dict__
def __iter__(self):
return iter(self.__dict__)
def __unicode__(self):
return unicode(repr(self.__dict__))
o = Mapping()
o.foo = "bar"
o['lumberjack'] = 'foo'
o.update({'a': 'b'}, c=44)
print 'lumberjack' in o
print o
In [187]: run mapping.py
True
{'a': 'b', 'lumberjack': 'foo', 'foo': 'bar', 'c': 44}
Like this
class CustomDictOne(dict):
def __init__(self,*arg,**kw):
super(CustomDictOne, self).__init__(*arg, **kw)
Now you can use the built-in functions, like dict.get() as self.get().
You do not need to wrap a hidden self._dict. Your class already is a dict.
Check the documentation on emulating container types. In your case, the first parameter to add should be self.
UserDict from the Python standard library is designed for this purpose.
Here is an alternative solution:
class AttrDict(dict):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.__dict__ = self
a = AttrDict()
a.a = 1
a.b = 2
This is my best solution. I used this many times.
class DictLikeClass:
...
def __getitem__(self, key):
return getattr(self, key)
def __setitem__(self, key, value):
setattr(self, key, value)
...
You can use like:
>>> d = DictLikeClass()
>>> d["key"] = "value"
>>> print(d["key"])
A python class that acts like dict
What's wrong with this?
Can anyone point out why most of the inheritance snippets look like the one below?
class CustomDictOne(dict):
def __init__(self):
self._mydict = {}
Presumably there's a good reason to inherit from dict (maybe you're already passing one around and you want a more specific kind of dict) and you have a good reason to instantiate another dict to delegate to (because this will instantiate two dicts per instance of this class.) But doesn't that sound incorrect?
I never run into this use-case myself. I do like the idea of typing dicts where you are using dicts that are type-able. But in that case I like the idea of typed class attributes even moreso - and the whole point of a dict is you can give it keys of any hashable type, and values of any type.
So why do we see snippets like this? I personally think it's an easily made mistake that went uncorrected and thus perpetuated over time.
I would rather see, in these snippets, this, to demonstrate code reuse through inheritance:
class AlternativeOne(dict):
__slots__ = ()
def __init__(self):
super().__init__()
# other init code here
# new methods implemented here
or, to demonstrate re-implementing the behavior of dicts, this:
from collections.abc import MutableMapping
class AlternativeTwo(MutableMapping):
__slots__ = '_mydict'
def __init__(self):
self._mydict = {}
# other init code here
# dict methods reimplemented and new methods implemented here
By request - adding slots to a dict subclass.
Why add slots? A builtin dict instance doesn't have arbitrary attributes:
>>> d = dict()
>>> d.foo = 'bar'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'dict' object has no attribute 'foo'
If we create a subclass the way most are doing it here on this answer, we see we don't get the same behavior, because we'll have a __dict__ attribute, causing our dicts to take up to potentially twice the space:
my_dict(dict):
"""my subclass of dict"""
md = my_dict()
md.foo = 'bar'
Since there's no error created by the above, the above class doesn't actually act, "like dict."
We can make it act like dict by giving it empty slots:
class my_dict(dict):
__slots__ = ()
md = my_dict()
So now attempting to use arbitrary attributes will fail:
>>> md.foo = 'bar'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'my_dict' object has no attribute 'foo'
And this Python class acts more like a dict.
For more on how and why to use slots, see this Q&A: Usage of __slots__?
I really don't see the right answer to this anywhere
class MyClass(dict):
def __init__(self, a_property):
self[a_property] = a_property
All you are really having to do is define your own __init__ - that really is all that there is too it.
Another example (little more complex):
class MyClass(dict):
def __init__(self, planet):
self[planet] = planet
info = self.do_something_that_returns_a_dict()
if info:
for k, v in info.items():
self[k] = v
def do_something_that_returns_a_dict(self):
return {"mercury": "venus", "mars": "jupiter"}
This last example is handy when you want to embed some kind of logic.
Anyway... in short class GiveYourClassAName(dict) is enough to make your class act like a dict. Any dict operation you do on self will be just like a regular dict.
The problem with this chunk of code:
class myDict(dict):
def __init__(self):
self._dict = {}
def add(id, val):
self._dict[id] = val
md = myDict()
md.add('id', 123)
...is that your 'add' method (...and any method you want to be a member of a class) needs to have an explicit 'self' declared as its first argument, like:
def add(self, 'id', 23):
To implement the operator overloading to access items by key, look in the docs for the magic methods __getitem__ and __setitem__.
Note that because Python uses Duck Typing, there may actually be no reason to derive your custom dict class from the language's dict class -- without knowing more about what you're trying to do (e.g, if you need to pass an instance of this class into some code someplace that will break unless isinstance(MyDict(), dict) == True), you may be better off just implementing the API that makes your class sufficiently dict-like and stopping there.
Don’t inherit from Python built-in dict, ever! for example update method woldn't use __setitem__, they do a lot for optimization. Use UserDict.
from collections import UserDict
class MyDict(UserDict):
def __delitem__(self, key):
pass
def __setitem__(self, key, value):
pass

Controlling getter and setter for a python's class

Consider the following class :
class Token:
def __init__(self):
self.d_dict = {}
def __setattr__(self, s_name, value):
self.d_dict[s_name] = value
def __getattr__(self, s_name):
if s_name in self.d_dict.keys():
return self.d_dict[s_name]
else:
raise AttributeError('No attribute {0} found !'.format(s_name))
In my code Token have some other function (like get_all() wich return d_dict, has(s_name) which tell me if my token has a particular attribute).
Anyway, I think their is a flaw in my plan since it don't work : when I create a new instance, python try to call __setattr__('d_dict', '{}').
How can I achieve a similar behaviour (maybe in a more pythonic way ?) without having to write something like Token.set(name, value) and get(name) each I want to set or get an attribute for a token.
Critics about design flaw and/or stupidity welcome :)
Thank !
You need to special-case d_dict.
Although of course, in the above code, all you do is replicate what any object does with __dict__ already, so it's pretty pointless. Do I guess correctly if you intended to special case some attributes and actally use methods for those?
In that case, you can use properties.
class C(object):
def __init__(self):
self._x = None
#property
def x(self):
"""I'm the 'x' property."""
return self._x
#x.setter
def x(self, value):
self._x = value
#x.deleter
def x(self):
del self._x
The special-casing of __dict__ works like this:
def __init__(self):
self.__dict__['d_dict'] = {}
There is no need to use a new-style class for that.
A solution, not very pythonic but works. As Lennart Regebro pointed, you have to use a special case for d_dict.
class Token(object):
def __init__(self):
super(Token,self).__setattr__('d_dict', {})
def __getattr__(self,name):
return self.a[name]
def __setattr__(self,name,value):
self.a[name] = value
You need to use new style classes.
the problem seems to be in time of evaluation of your code in __init__ method.
You could define __new__ method and initialize d_dict variable there instead of __init__.
Thats a bit hackish but it works, remember though to comment it as after few months it'll be total magic.
>>> class Foo(object):
... def __new__(cls, *args):
... my_cls = super(Foo, cls).__new__(cls, *args)
... my_cls.d_dict = {}
... return my_cls
>>> f = Foo()
>>> id(f.d_dict)
3077948796L
>>> d = Foo()
>>> id(d.d_dict)
3078142804L
Word of explanation why I consider that hackish: call to __new__ returns new instance of class so then d_dict initialised in there is kind of static, but it's initialised with new instance of dictionary each time class is "created" so everything works as you need.
It's worth remembering that __getattr__ is only called if the attribute doesn't exist in the object, whereas __setattr__ is always called.
I think we'll be able to say something about the overall design of your class if you explain its purpose. For example,
# This is a class that serves as a dictionary but also has user-defined methods
class mydict(dict): pass
# This is a class that allows setting x.attr = value or getting x.attr:
class mysetget: pass
# This is a class that allows setting x.attr = value or getting x.attr:
class mygetsethas:
def has(self, key):
return key in self.__dict__
x = mygetsethas()
x.a = 5
print(x.has('a'), x.a)
I think the last class is closest to what you meant, and I also like to play with syntax and get lots of joy from it, but unfortunately this is not a good thing. Reasons why it's not advisable to use object attributes to re-implement dictionary: you can't use x.3, you conflict with x.has(), you have to put quotes in has('a') and many more.

Categories