In building a class with an out line like below I would like the behaviour of the for loops to, if done once: just give the keys as normal an then move on to the next line of code. But if a second loop is set up inside the first loop it would give the keys on the first loop and then ea value in the sequences in the second loop. The problem I can't figure out is how to set up this under iter.
class MyClass():
def __init__(self):
self.cont1 = [1,2,3,4]
self.cont2 = ('a','b','c')
def __iter__(self):
pass # ???????
Something like this:
dct = dict(container1=[5,6,7,8], container2=('a','b','c')
if one loop is used:
for ea in dct:
print(ea)
print("Howdy")
'containter1'
'containter2'
Howdy
If a nest loop is used:
for ea in dct:
print(ea)
for i in dct.get(ea):
print(i)
'container1'
5
6
...
'container2'
a
b
c
To answer your immediate question, you could just copy how dictionaries implement dict.get and dict.__iter__:
class MyClass():
def __init__(self):
self.cont1 = [1,2,3,4]
self.cont2 = ('a','b','c')
def __iter__(self):
for attr in dir(self):
if not attr.startswith('_') and attr != 'get':
yield attr
def get(self, key):
return getattr(self, key)
It's not a very good approach, however. Looking at the attributes of your object at runtime isn't a good idea, because it will break when you subclass and it will add needless complexity. Instead, just use a dictionary internally:
class MyClass():
def __init__(self):
self.container = {
'cont1': [1, 2, 3, 4],
'cont2': ('a', 'b', 'c')
}
def __iter__(self):
return iter(self.container)
def get(self, key):
return self.container.get(key)
You can do this with a second class like this
class MyClass():
def __init__(self):
self.data = [MyClass2({'cont1' : [1,2,3,4]}),MyClass2({'cont2' : ('a','b','c')})]
def __iter__(self):
for item in self.data:
yield item
class MyClass2():
def __init__(self, mydict):
self.d = mydict
def __iter__(self):
for item in self.d.values():
for value in item:
yield value
def __repr__(self):
return(list(self.d.keys())[0])
m = MyClass()
for k in m:
print(k)
for val in k:
print(val)
You cannot do that simply by implementing __iter__. __iter__ should return an iterator, that is, an object that keeps the state of an iteration (the current position in a sequence of items) and has a method next that returns with each invocation the next item in the sequence.
If your object has nested sequences you can implement an iterator that will traverse only the external sequence, or one that will traverse both
the external and the internal sequences - in a depth-first or a breath-first fashion - but it does not make sense to use nested loops on the same iterable:
# iterate over every item in myobj
for x in myobj:
...
# iterate over every item again? not likely what you want!
for y in myobj:
A more likely situation is:
for x in myob:
...
for y in x:
...
How would you feel about this:
class MyClass():
def __init__(self):
self.cont1 = [1,2,3,4]
self.cont2 = ('a','b','c')
self.conts = {'container1':self.cont1, 'container2':self.cont2}
def __iter__(self):
return self.conts.iteritems()
dct = MyClass()
print('One loop')
for mi in dct:
print(mi)
print('='*40)
print('Nested loops')
for name, values in dct:
print(name)
for i in values:
print(i)
Which outputs:
One loop
container1
container2
========================================
Nested loops
container1
1
2
3
4
container2
a
b
c
Update
I don't know that I would really recommend this, but this seems to more closely fit what the OP wants:
class MyIterator(object):
def __init__(self, name, values):
self.vals = iter(values)
self.name = name
def __iter__(self):
return self.vals
def __str__(self):
return self.name
class MyClass():
def __init__(self):
self.cont1 = [1,2,3,4]
self.cont2 = ('a','b','c')
self.conts = [MyIterator('container1', self.cont1),
MyIterator('container2', self.cont2)]
def __iter__(self):
return iter(self.conts)
dct = MyClass()
for mi in dct:
print(mi)
for i in mi:
print(i)
This is the only way I can think of to be able to print the name and then iterate over it as the values list. This works by overriding the __str__ method to change how the object gets "stringified". But as I said earlier, I think you would be better served with the first part of the answer.
Sorry, just realized nauer's answer already showed something like this.
Related
I'm trying to modify the following code, so that MyCollection will wrap a dictionary. I still have to implement the iter and next methods in order to have the "for element in collection" functionality. I know that can be easily done by iterating through the values, but I am required to do it like this. Can someone help me ?
class MyCollection:
def __init__(self):
self._data = [] // should be {}
def __iter__(self):
'''
Return an iterator
'''
self._iterPoz = 0
return self
def __next__(self):
'''
Returns the next element of the iteration
'''
if self._iterPoz >= len(self._data):
raise StopIteration()
rez = self._data[self._iterPoz]
self._iterPoz = self._iterPoz + 1
return rez
This begins with a design decision. When you iterate MyCollection what data do you want? If its the values of the contained dictionary you can return its iterator and then you don't implement __next__ at all.
class MyCollection:
def __init__(self):
self._data = {}
def __iter__(self):
'''
Return an iterator of contained values
'''
return iter(self._data.values())
I'm just implementing a class that requires an attribute to store a reference of another attribute as a cursor. See the following:
class foo:
def __init__(self):
self.egg=[4,3,2,1,[4,3,2,1]]
self.spam=#some reference or pointer analog represent self.egg[4][2], for example
def process(self):
# do something on self.egg[self.spam]
pass
I don't want a dict because self.spam should only represent one item, and using a dict I would have to consume indefinite unnecessary memory. Is there some pythonic way to implement self.spam above?
You could store the indices in self.spam, and use a property to access the value from self.egg given the current value of self.spam:
class Foo(object):
def __init__(self):
self.egg = [4,3,2,1,[4,3,2,1]]
self.spam = (4,2)
def process(self):
# do something on self.egg[self.spam]
print(self.eggspam)
pass
#property
def eggspam(self):
result = self.egg
for item in self.spam:
result = result[item]
return result
f = Foo()
f.process()
# 2
f.spam = (1,)
f.process()
# 3
I am starting OOP with Python 3 and I find the concept of property really interesting.
I need to encapsulate a private list, but how could I use this paradigm for lists?
Here's my naive try:
class Foo:
""" Naive try to create a list property.. and obvious fail """
def __init__(self, list):
self._list = list
def _get_list(self, i):
print("Accessed element {}".format(i))
return self._list[i]
def _set_list(self, i, new):
print("Set element {} to {}".format(i, new))
self._list[i] = new
list = property(_get_list, _set_list)
This doesn't behave as expected and even makes python crash when I try the following code. This is the fictive behavior I would like Foo to exhibit:
>>> f = Foo([1, 2, 3])
>>> f.list
[1, 2, 3]
>>> f.list[1]
Accessed element 1
2
>>> f.list[1] = 12
Set element 1 to 12
>>> f.list
[1, 12, 3]
import collections
class PrivateList(collections.MutableSequence):
def __init__(self, initial=None):
self._list = initial or []
def __repr__(self):
return repr(self._list)
def __getitem__(self, item):
print("Accessed element {}".format(item))
return self._list[item]
def __setitem__(self, key, value):
print("Set element {} to {}".format(key, value))
self._list[key] = value
def __delitem__(self, key):
print("Deleting element {}".format(key))
del self._list[key]
def __len__(self):
print("Getting length")
return len(self._list)
def insert(self, index, item):
print("Inserting item {} at {}".format(item, index))
self._list.insert(index, item)
class Foo(object):
def __init__(self, a_list):
self.list = PrivateList(a_list)
Then runnning this:
foo = Foo([1,2,3])
print(foo.list)
print(foo.list[1])
foo.list[1] = 12
print(foo.list)
Outputs:
[1, 2, 3]
Accessed element 1
2
Set element 1 to 12
[1, 12, 3]
There are some problems in your code. They might not be the only problems but fixing them would bring you further:
Properties are for new style classes. They are derived from object:
class Foo(object):
The getter (the first argument to property will be called without argument. So _get_list can't have the second argument i. The same applies to _set_list it can only have one argument, not two. (self is implicit and does not count here.)
How do you pythonically set multiple properties without referencing them individually? Below is my solution.
class Some_Class(object):
def __init__(self):
def init_property1(value): self.prop1 = value
def init_property2(value): self.prop2 = value
self.func_list = [init_property1, init_property2]
#property
def prop1(self):
return 'hey im the first property'
#prop1.setter
def prop1(self, value):
print value
#property
def prop2(self):
return 'hey im the second property'
#prop2.setter
def prop2(self, value):
print value
class Some_Other_Class(object):
def __init__(self):
myvalues = ['1 was set by a nested func','2 was set by a nested func']
some_class= Some_Class()
# now I simply set the properties without dealing with them individually
# this assumes I know how they are ordered (in the list)
# if necessary, I could use a map
for idx, func in enumerate(some_class.func_list):
func(myvalues[idx])
some_class.prop1 = 'actually i want to change the first property later on'
if __name__ == '__main__':
test = Some_Other_Class()
this became necessary to do when I had many many properties to initialize with user defined values. My code otherwise would look like a giant list of setting each property individually (very messy).
Note that many people have good answers below and I think I have reached a good solution. This is a re-edit mostly trying to clearly state the question. But, if anyone has a better approach please share!
just use the #property decorator
>>> class A:
... a=2
... #property
... def my_val(self,val=None):
... if val == None:return self.a
... self.a = val
...
>>> a=A()
>>> a.my_val
2
>>> a.my_val=7
>>> a.my_val
7
something like this?
if you only want to allow setting then dont give it a default val
>>> class A:
... a=2
... #property
... def my_val(self,val):
... self.a = val
...
>>> a=A()
>>> a.my_val
<Exception>
>>> a.my_val=7
>>> a.a
7
or if you only want to allow retrieval just ommit the 2nd arg
>>> class A:
... a=2
... #property
... def my_val(self):
... return self.a
...
...
>>> a=A()
>>> a.my_val
2
>>> a.my_val=7
<Exception>
I ... finally think I know what you're trying to do, and you don't need to do it the way you're approaching it. Let me take a stab at this.
class someclass(object):
def __init__(self):
func_list = [self.setter1, self.setter2]
value_list = [1, 2]
# These lines don't need to be this complicated.
# for ind in range(len(func_list)):
# func_list[ind](value_list[ind])
for idx, func in enumerate(func_list):
func(value_list[idx])
# Or even better
for idx, (func, val) in enumerate(zip(func_list, value_list)):
func(val)
def setter1(self, value):
self.a = value
def setter2(self, value):
self.b = value
It's worth pointing out that the idx variable and enumerate calls are superfluous in the second form, but I wasn't sure if you need that elsewhere.
If you look up the property in the object dict, you will get the property descriptor (if any), and likewise with the class; e.g.
a = SomeClass()
descriptor = a.__dict__.get('descriptor', type(a).__dict__.get('descriptor'))
Assuming that descriptor is a descriptor, it will have the following methods:
['deleter', 'fdel', 'fget', 'fset', 'getter', 'setter']
Note the fget and fset.
Is there any way to make a list of classes behave like a set in python?
Basically, I'm working on a piece of software that does some involved string comparison, and I have a custom class for handling the strings. Therefore, there is an instance of the class for each string.
As a result, I have a large list containing all these classes. I would like to be able to access them like list[key], where in this case, the key is a string the class is based off of (note: the string will never change once the class is instantiated, so it should be hashable).
It seems to me that I should be able to do this somewhat easily, by adding something like __cmp__ to the class, but either I'm being obtuse (likely), or I'm missing something in the docs.
Basically, I want to be able to do something like this (Python prompt example):
>>class a:
... def __init__(self, x):
... self.var = x
...
>>> from test import a
>>> cl = set([a("Hello"), a("World"), a("Pie")])
>>> print cl
set([<test.a instance at 0x00C866C0>, <test.a instance at 0x00C866E8>, <test.a instance at 0x00C86710>])
>>> cl["World"]
<test.a instance at 0x00C866E8>
Thanks!
Edit Some additional Tweaks:
class a:
... def __init__(self, x):
... self.var = x
... def __hash__(self):
... return hash(self.var)
...
>>> v = a("Hello")
>>> x = {}
>>> x[v]=v
>>> x["Hello"]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'Hello'
>>> x["Hello"]
Just write a class that behaves a bit like a mapping:
class ClassDict(object):
def __init__(self):
self.classes = {}
def add(self, cls):
self.classes[cls.__name__] = cls
def remove(self, cls):
if self.classes[cls.__name__] == cls:
del self.classes[cls.__name__]
else:
raise KeyError('%r' % cls)
def __getitem__(self, key):
return self.classes[key]
def __repr__(self):
return 'ClassDict(%s)' % (', '.join(self.classes),)
class C(object):
pass
class D(object):
pass
cd = ClassDict()
cd.add(C)
cd.add(D)
print cd
print cd['C']
Why don't you just do:
>>> v = MyStr("Hello")
>>> x = {}
>>> x[v.val]=v
>>> x["Hello"]
MyStr("Hello")
Why go through all the trouble of trying to create a hand-rolled dict that uses different keys than the ones you pass in? (i.e. "Hello" instead of MyStr("Hello")).
ex.
class MyStr(object):
def __init__(self, val):
self.val = str(val)
def __hash__(self):
return hash(self.val)
def __str__(self):
return self.val
def __repr__(self):
return 'MyStr("%s")' % self.val
>>> v = MyStr("Hello")
>>> x = {}
>>> x[str(v)]=v
>>> x["Hello"]
MyStr("Hello")
Set and dict use the value returned by an object's __hash__ method to look up the object, so this will do what you want:
>>class a:
... def __init__(self, x):
... self.var = x
...
... def __hash__(self):
... return hash(self.var)
As I remember "set" and "dict" uses also __hash__
From Python 2.x doc:
A dictionary’s keys are almost arbitrary values. Values that are not hashable, that is, values containing lists, dictionaries or other mutable types (that are compared by value rather than by object identity) may not be used as keys.
Do you want something like this
class A(object):
ALL_INSTANCES = {}
def __init__(self, text):
self.text = text
self.ALL_INSTANCES[self.text] = self
a1 = A("hello")
a2 = A("world")
print A.ALL_INSTANCES["hello"]
output:
<__main__.A object at 0x00B7EA50>