I have a method that needs to be called on a lot of objects, and the code is getting extremely long because of that. I want to have a way of looping through objects names so the number of lines will be reduced.
c1.front_turn_clock()
c2.front_turn_clock()
c3.front_turn_clock()
c4.front_turn_clock()
c6.front_turn_clock()
c7.front_turn_clock()
c8.front_turn_clock()
c9.front_turn_clock()
c10.front_turn_clock()
c11.front_turn_clock()
c13.front_turn_clock()
c14.front_turn_clock()
c15.front_turn_clock()
c16.front_turn_clock()
c17.front_turn_clock()
c19.front_turn_clock()
c20.front_turn_clock()
c21.front_turn_clock()
c23.front_turn_clock()
c25.front_turn_clock()
The fix should look something like this:
while x <= 25:
cx.front_turn_clock()
x += 1
You can easily make the code more expandable and smaller by making a list of objects
clocks = []
clocks.append(newclockobj)
...
for i in range(len(clocks)):
clocks[i].front_turn_clock()
Also you can use enumerate instead of range(len())
or your original While idea
Put c1-c25 in a list, then loop through each item and call front_turn_clock() on them
I don't recommend this but the closest thing to what you want is probably eval which takes your string literal and executes it as if it's code.
while x <= 25:
eval('c%i.front_turn_clock()'%i)
x += 1
You should store the objects in a list and traverse the list.
for object in objects:
object.front_turn_clock()
You can also do this:
for i in range(1, 26):
globals()['c'+str(i)].front_turn_clock()
Or like this:
import sys
thismdl = sys.modules[__name__]
getattr(thismdl, 'c'+str(i)).front_turn_clock()
Both ways will try to get the instance at memory and execute the function.
I don't recommend though, using lists is more pythonic.
Related
I want to be able to change what something does in a for loop
here a very simple example
for i in range(10):
print(i)
this will print 0 up to 9 and not include 10
and ofc i want 1 to 10 not 0 to 9
to fix this i would like to say:
i+1 for i in range(10):
print(i)
but i cant
if i did list comprehension i can do:
list0 = [i+1 for i in range(10)]
this is very handy
now i have to either do
for i in range(1, 10+1):
which is very annoying
or do
print(i+1)
but if i used i 10 times i'd have to change them all
or i could say:
for i in range(10):
i += 1
these methods are all not very nice, im just wondering if this neat way im looking for exists at all
thanks.
You ask if there exists any way to change the value received from an iterable in a for loop. The answer is yes; this can be accomplished in one of two ways. I'll continue to use your example with range to demonstrate this, but do note that I am in no way suggesting that these are ideal ways of solving that particular problem.
The first method is using the builtin map:
for i in map(lambda x: x + 1, range(10)):
map accepts a callable and an iterable, and will lazily apply the given callable to each element produced by the iterable. Do note that since this involves an additional function call during each iteration, this technique can incur a noticeable runtime penalty compared to performing the same action within the loop body.
The second method is using a generator expression (or, alternatively, any other flavor of list/set/dict compression):
for i in (x + 1 for x in range(10)):
As with map, using a generator will lazily produce transformed elements from the given iterable. Do note that if you opt to use a comprehension instead, the entire collection will be constructed upfront, which may be undesirable.
Again, for incrementing the values produced by range, neither of these are ideal. Simply using range(1, 11) is the natural solution for that.
I have a list with 1000 items in it but only want to call a certain range of them.
class myClass():
def event(self):
#do stuff
my_list = [myClass(i) for i in range(1000)]
#incorrect part:
my_list[0 - 10].event()
Meaning that I am trying to call "event" for only the first 9 objects. What would be the correct way to write this?
Do this:
for obj in my_list[:9]:
obj.event()
Note since you just want the first 9 objects to be called, you need to use indexes 0-8 i.e. 0,1,2,3,4,5,6,7,8
[x.event() for x in my_list[:9]]
or
list(map(lambda x: x.event(), my_list[:9]))
or, as #khredos suggests,
for obj in my_list[:9]:
obj.event()
If myClass.event() does return something (and you want to keep the result), the first is the most pythonic. If, on the other hand, myClass.event() involves side effects (and you do not want to keep the result, of course), go for the the third approach.
I have some trouble with list comprehension, I think I already know how to use it well but certainly I don't.
So here is my code:
vector1=[x for x in range(0,351,10)]
first=list(range(0,91))
second=list(range(100,181))
third=list(range(190,271))
fourth=list(range(280,351))
Quadrants=first+second+third+fourth
string=['First']*91+['Second']*81+['Third']*81+['Fourth']*71
vector2=dict(zip(Quadrants,string))
Quadrant=[]
for n in range (len(vector1)):
Quadrant+=[vector2[vector1[n])]]
So i want to do the for_loop with list comprehension, but i can't... I tried this:
Quadrant=[y3 for y3 in [vector2[vector1[i]]] for i in range (len(vector1))]
Here's the code you're trying to convert to a listcomp:
Quadrant=[]
for n in range (len(vector1)):
Quadrant+=[y[vector1[n]]]
First, you have to convert that into a form using append. There's really no reason to build a 1-element list out of y[vector1[n]] in the first place, so just scrap that and we have something we can appenddirectly:
Quadrant=[]
for n in range(len(vector1)):
Quadrant.append(y[vector1[n]])
And now, we have something we can convert directly into a list comprehension:
Quadrant = [y[vector1[n]] for n in range(len(vector1))]
That's all there is to it.
However, I'm not sure why you're doing for n in range(len(vector1)) in the first place if the only thing you need n for is vector1[n]. Just loop over vector1 directly:
Quadrant=[]
for value in vector1:
Quadrant.append(y[value])
Which, again, can be converted directly:
Quadrant = [y[value] for value in vector1]
However, all of this assumes that your original explicit loop is correct in the first place, which obviously it isn't. Your vector1 is a dict, not a list. Looping over it the keys from 0 to len(vector1) is just going to raise KeyErrors all over the place. Changing it to loop directly over vector1 is going to solve that problem, but it means you're looping over the keys. So… I have no idea what your code was actually trying to do, but get the simple but verbose version right first, and you can probably convert it to a comprehension just as easily as the above.
I have a list of instances of a class A
class A:
def __init__(self,ox,oy):
self.x=ox
self.y=oy
list1=[A(3,0),A(5,0),A(7,3),......]
Now I need to find out the instance in the list which has y' as non-zero - and apply that value to all the other members in the list.
It is given that only one unique member will have y as non-zero.
With the usual for-loop we would need to iterate the list twice - with or without comprehension.
Is there a way to achieve this any better.
I have not used filter and map much but feel there may be a better option.
Help is appreciated.
No, there isn't. At least two loops would be required no matter how it was implemented.
import numpy
a = numpy.array([[3,0],[5,0],[7,3]])
zero_mask = a[:,1] == 0
a[zero_mask] = a[~zero_mask][0]
unfortunately it does not use your A class ...
I'm a looking to initialize an array/list of objects that are not empty -- the class constructor generates data. In C++ and Java I would do something like this:
Object lst = new Object[100];
I've dug around, but is there a Pythonic way to get this done?
This doesn't work like I thought it would (I get 100 references to the same object):
lst = [Object()]*100
But this seems to work in the way I want:
lst = [Object() for i in range(100)]
List comprehension seems (intellectually) like "a lot" of work for something that's so simple in Java.
There isn't a way to implicitly call an Object() constructor for each element of an array like there is in C++ (recall that in Java, each element of a new array is initialised to null for reference types).
I would say that your list comprehension method is the most Pythonic:
lst = [Object() for i in range(100)]
If you don't want to step on the lexical variable i, then a convention in Python is to use _ for a dummy variable whose value doesn't matter:
lst = [Object() for _ in range(100)]
For an equivalent of the similar construct in Java, you can of course use *:
lst = [None] * 100
You should note that Python's equvalent for Java code
(creating array of 100 null references to Object):
Object arr = new Object[100];
or C++ code:
Object **arr = new Object*[100];
is:
arr = [None]*100
not:
arr = [Object() for _ in range(100)]
The second would be the same as Java's:
Object arr = new Object[100];
for (int i = 0; i < arr.lenght; i++) {
arr[i] = new Object();
}
In fact Python's capabilities to initialize complex data structures are far better then Java's.
Note:
C++ code:
Object *arr = new Object[100];
would have to do as much work as Python's list comprehension:
allocate continuous memory for 100 Objects
call Object::Object() for each of this Objects
And the result would be a completely different data structure.
I think the list comprehension is the simplest way, but, if you don't like it, it's obviously not the only way to obtain what you desire -- calling a given callable 100 times with no arguments to form the 100 items of a new list. For example, itertools can obviously do it:
>>> import itertools as it
>>> lst = list(it.starmap(Object, it.repeat((), 100)))
or, if you're really a traditionalist, map and apply:
>>> lst = map(apply, 100*[Object], 100*[()])
Note that this is essentially the same (tiny, both conceptually and actually;-) amount of work it would take if, instead of needing to be called without arguments, Object needed to be called with one argument -- or, say, if Object was in fact a function rather than a type.
From your surprise that it might take "as much as a list comprehension" to perform this task, you appear to think that every language should special-case the need to perform "calls to a type, without arguments" over other kinds of calls to over callables, but I fail to see what's so crucial and special about this very specific case, to warrant treating it differently from all others; and, as a consequence, I'm pretty happy, personally, that Python doesn't single this one case out for peculiar and weird treatment, but handles just as regularly and easily as any other similar use case!-)
lst = [Object() for i in range(100)]
Since an array is it's own first class object in python I think this is the only way to get what you're looking for. * does something crazy.