In the following code I create a list of three objects and use the variable a,b,c to store the first, second and then the third object by their id's but when I try to store the third object in variable c, it stores a list of the second and third object.
class Obj1():
id='obj1'
class Obj2():
id='obj2'
class Obj3():
id='obj3'
list1=[Obj1(),Obj2(),Obj3()]
a=list1[id=="obj1"]
print a
b=list1[id!='obj1']
print b
c=list1[id!='obj1'and id!='obj2':]
print c
When I run this code I get :
<__main__.Obj1 instance at 0x02AD5DA0>
<__main__.Obj2 instance at 0x02AD9030>
[<__main__.Obj2 instance at 0x02AD9030>, <__main__.Obj3 instance at 0x02AD90A8>]
why does variable c contain two objects?
Using a dictionary is probably the best idea in this case, as mentioned by Medhat. However, you can do things in a similar way to what you attempted using list comprehensions:
a = [e for e in list1 if e.id == "obj1"]
print a
b = [e for e in list1 if e.id != "obj1"]
print b
c = [e for e in list1 if e.id != "obj1" and e.id != "obj2"]
# Or:
# [e for e in list1 if e.id not in ("obj1", "obj2")]
print c
You should use a dictionary instead:
obj1 = Obj1()
obj2 = Obj2()
obj3 = Obj3()
list1 = {obj1.id: obj1, obj2.id: obj2, obj3.id: obj3}
Then access your objects like this:
a = list1[obj1.id]
b = list1[obj2.id]
c = list1[obj3.id]
id!='obj1'and id!='obj2' returns true which equals 1 in Python, that is to say, c=list1[id!='obj1'and id!='obj2':] equals c=list1[1:] , which of course has two objects.
BTW, id is the name of a built-in function. Please avoid using it as a name of varible.
Your list1 contains 3 elements:
>>> list1
[<__main__.Obj1 instance at 0x103437440>, <__main__.Obj2 instance at 0x1034377a0>, <__main__.Obj3 instance at 0x103437320>]
>>> id!='obj1'
True
>>> id!='obj2'
True
>>> True and True
True
>>> list1[True:]
[<__main__.Obj2 instance at 0x1034377a0>, <__main__.Obj3 instance at 0x103437320>]
>>>
True is 1 and False is 0 index:
Here is the example:
>>> ls = [1,2]
>>> ls[True]
2
>>> ls[False]
1
>>>
So, list[True:] is equal to list[1:] which is from first element till last.
In your case the the last two elements in the list1.
Related
Suppose I co-assign two python variables to zero:
>>> x = y = 0
When I now assign to one value it changes independently of the other:
>>> y = 2
>>> x
0
>>> y
2
Now suppose I co-assign two python variables to an empty set:
>>> a = b = set([])
When I now add to one set, it changes the value of the other. Why?
>>> b.add(2)
>>> a
set([2])
>>> b
set([2])
As commments to OP answer are just implicit clues I am making them explicit:
y = 0
x = y
Names x and y reference the same int object with 0 value.
int objects are immutable. As you can't alter them, it actually doesn't
matter what object with zero value you are pointing to, if there were several.
When you assign y again with
y = 2
y now refers to a different int object with value 2.
x still refers to the old 0 value object.
If you consider mutable types like sets,
b = set()
a = b
a and b reference the same empty set object.
If you change the set object with an in-place assignment, or using an object method that changes it in-place, the object remains the same (but with its value content changed).
So you can see the changed object either through a or through b.
However, to compare apples with apples and follow the int sample, if you make
b = set() # a new empty set
b becomes a new different set empty object, while a still references the original.
>>> a = set()
>>> b = a
>>> id(a) == id(b)
True
>>> b = set()
>>> id(a) == id(b)
False
>>>
So behaviour is actually the same in both cases. But in int case you can't make changes to the object value.
I wish to iterate over a zip of objects and floats together using a parent class function. Sometimes the subclasses have three objects and three floats in a list, this works fine:
L = [A,B,C]
F = [1,2,3]
for f, o in zip(L,F):
# do stuff
Sometimes the subclass has one object and one float
L = A
F = 1
for f, o in zip(L,F):
# TypeError
This throws an exception because F is not iterable.
If I try:
F = list(F)
This works for 2 or more floats but throws an exception for a single float (it's still not iterable :)).
If I try:
F = [F]
This solves the 1 float case but now returns a nested list when there are two or more floats!
a = [1,2]
[a] = [[1,2]]
Is there a simple builtin way to receive either a single float or list of floats and return a single flat list? Or do I need something like itertools.chain?
Just check if you already have a list, and if not create one with a single element:
f = f if isinstance(f,list) else [f]
For example:
>>> f = 1
>>> f = f if isinstance(f,list) else [f]
>>> f
[1]
>>> f = [1,2]
>>> f = f if isinstance(f,list) else [f]
>>> f
[1, 2]
Note, I'm assuming your using lists but you could use collections.Iterable for a more generic solution.
Further note, I don't know where F/f came from, but ideally it would have been initially created this way rather than fixing it now.
You can try a = a if isinstance(a, collections.Iterable) else [a]. This makes sure a is either iterable or converts it to list. Also you need not convert a, you could assign the result to another variable.
You can design your objects so that they have a builtin __iter__ method along with __len__:
class CertainObject:
def __init__(self):
self.variables = [23, 4, 2]
def __len__(self):
return len(self.variables)
def __iter__(self):
for i in self.variables:
yield i
Case 1(Multiple objects):
L = [CertainObject(), CertainObject(),CertainObject()]
F = [1,2,3]
for f, o in zip(L,F):
pass
Case 1 (singe object):
A = CertainObject()
F = 1
for a, b in zip(A, [F]*len(A)):
print(a, b)
With __iter__, iterating over the instance of the object will not raise an error.
You can use your case that returns a nested list, but then cast the list as a numpy array and use the flatten() function, then re-cast as a list if you want it that way.
a = list(np.array(a).flatten())
This way, a = 1 will turn into [1], a = [1, 2] will turn into [1, 2], and a = [[1, 2]] will turn into [1, 2], so all outputs are lists.
Use extend
L = []
F = []
L.extend([A])
F.extend([B])
for f,o in zip(L,F):
# do stuff
This should not give you problems both with one item lists and with more than one item lists (it won't be nested).
example 1 item:
L = []
F = []
A = 1
B = 1
L.extend([A])
F.extend([B])
for f,o in zip(L,F):
print(f,o)
otput
1 1
Example more items
L = []
F = []
A = [1,2,3]
B = [1,2,3]
L.extend([A])
F.extend([B])
for f,o in zip(L,F):
print(f,o)
output
1 1
2 2
3 3
I want to compare 2 lists, say A and B.
A = [1,2,3]
B = [3,1,2]
I want to write a function to check if all the items in A are present in B or not.
i.e. the function should return True for the example above.
Note: The items of my lists are non-hashable objects.
For this, I wrote this code:
for elem in A:
if elem not in B:
return False
return True
The code works fine.
But I want a more efficient and a more pythonic way to do this.
You may use all
all(i in B for i in A)
Example:
>>> A = [1,2,3]
>>> B = [3,1,2]
>>> all(i in B for i in A)
True
>>> B = [3,1,4]
>>> all(i in B for i in A)
False
>>>
You could use Counter, which runs in O(n) time.
from collections import Counter
A = [1,2,3]
B = [3,1,2]
print(Counter(A) == Counter(B)) # Prints True
I think using set is a good solution for this task :
In [2]: A = [1,2,3]
In [3]: B = [3,1,2]
In [5]: set(A)==set(B)
Out[5]: True
This code will have O(n) complexity for more information you can check this link
If you don't want to use set you can try to sort lists :
def cmp(A,B):
A1 = sorted(A)
B1 = sorted(B)
for item in A1:
if binary_search(B1,item)==-1:
return False
for item in B1:
if binary_search(A1,item)==-1:
return False
return True
I'm comparing a few algorithms.Each algorithms(class) received the same list. The problem is that first class affect on list and other classes cannot work on that list.
Is there any clever way to do this?
Here is a code:
Lista = []
start = 54
for i in range(25):
liczba = random.randint(1,179)
if liczba not in Lista and liczba != start:
Lista.append(liczba)
else:
i -= 1
print "Lista: ", Lista
x = SSTF(Lista)
x.Symulacja(91) #<----- OK!
y = FCFS(Lista)
y.Symulacja(25) #<----- FCFS received epty list.
z = SCAN()
z.Symulacja(start, Lista)
w = C_SCAN()
w.Symulacja(start, Lista)
results = Result()
results.Add(x)
results.Add(y)
print Results
SSTF is removing elements from list which received, FCFS the same. So after doing SSTF algorithm FCFS reveived empty list. I can't understand why this list is affected. I'm not working on List "Lista", but in init of SSTF i'm assigning "Lista" to other list.
Sorry if my problem is not a clear. I'm learning python and that problem hits me many times.
x = SSTF(Lista[:])
y = FCFS(Lista[:])
....
etc...
Your problem:
def SSTF(Lista):
Listb = Lista
means that Listb is still the same Lista, because this is a reference to the same object.
To avoid it, copy the list entirely, using [:] slice notation (Python dictionaries have the .copy() method, which is clearer).
Basically, any time you copy a reference to an object from one variable to another variable, you are ending up with two pointers to the same object so changes to that object will be seen by both variable names. Yeah, sorry about the terminology.
The pointer is shown by the id() code below. Notice how li == li2 != li3
>>>li = [1,2]
>>>li2 = li
>>>li3 = li[:]
>>>li2.append(3)
>>>print "li:", id(li), li
>>>print "li2:", id(li2), li2
>>>print "li3:", id(li3), li3
li: 4385880760 [1, 2, 3]
li2: 4385880760 [1, 2, 3]
li3: 4385924376 [1, 2]
This is not the case with things like numbers or strings. Check out also the concept of 'immutable'.
>>> a = "xxx"
>>> b = a
>>> b = b +"y"
>>> print a, b
xxx xxxy
>>> a = 1
>>> b = a
>>> b =b+1
>>> print a, b
1 2
If you need to copy your custom classes' instances, look into the copy module, but keep in mind that instance attributes are either shared or copied, depending on using copy.copy(x) or copy.deepcopy(x). In practice, this is rarely necessary, but I got bitten many times by Lista type issues on built-in collection classes before I learned my lesson.
I have three collection.deques and what I need to do is to iterate over each of them and perform the same action:
for obj in deque1:
some_action(obj)
for obj in deque2:
some_action(obj)
for obj in deque3:
some_action(obj)
I'm looking for some function XXX which would ideally allow me to write:
for obj in XXX(deque1, deque2, deque3):
some_action(obj)
The important thing here is that XXX have to be efficient enough - without making copy or silently using range(), etc. I was expecting to find it in built-in functions, but I found nothing similar to it so far.
Is there such thing already in Python or I have to write a function for that by myself?
Depending on what order you want to process the items:
import itertools
for items in itertools.izip(deque1, deque2, deque3):
for item in items:
some_action(item)
for item in itertools.chain(deque1, deque2, deque3):
some_action(item)
I'd recommend doing this to avoid hard-coding the actual deques or number of deques:
deques = [deque1, deque2, deque3]
for item in itertools.chain(*deques):
some_action(item)
To demonstrate the difference in order of the above methods:
>>> a = range(5)
>>> b = range(5)
>>> c = range(5)
>>> d = [a, b, c]
>>>
>>> for items in itertools.izip(*d):
... for item in items:
... print item,
...
0 0 0 1 1 1 2 2 2 3 3 3 4 4 4
>>>
>>> for item in itertools.chain(*d):
... print item,
...
0 1 2 3 4 0 1 2 3 4 0 1 2 3 4
>>>
The answer is in itertools
itertools.chain(*iterables)
Make an iterator that returns elements from the first iterable until
it is exhausted, then proceeds to the
next iterable, until all of the
iterables are exhausted. Used for
treating consecutive sequences as a
single sequence. Equivalent to:
def chain(*iterables):
# chain('ABC', 'DEF') --> A B C D E F
for it in iterables:
for element in it:
yield element
Call me crazy, but why is using itertools thought to be necessary? What's wrong with:
def perform_func_on_each_object_in_each_of_multiple_containers(func, containers):
for container in containers:
for obj in container:
func(obj)
perform_func_on_each_object_in_each_of_multiple_containers(some_action, (deque1, deque2, deque3)
Even crazier: you probably are going to use this once. Why not just do:
for d in (deque1, deque2, deque3):
for obj in d:
some_action(obj)
What's going on there is immediately obvious without having to look at the code/docs for the long-name function or having to look up the docs for itertools.something()
Use itertools.chain(deque1, deque2, deque3)
How about zip?
for obj in zip(deque1, deque2, deque3):
for sub_obj in obj:
some_action(sub_obj)
Accepts a bunch of iterables, and yields the contents for each of them in sequence.
def XXX(*lists):
for aList in lists:
for item in aList:
yield item
l1 = [1, 2, 3, 4]
l2 = ['a', 'b', 'c']
l3 = [1.0, 1.1, 1.2]
for item in XXX(l1, l2, l3):
print item
1
2
3
4
a
b
c
1.0
1.1
1.2
It looks like you want itertools.chain:
"Make an iterator that returns elements from the first iterable until it is exhausted, then proceeds to the next iterable, until all of the iterables are exhausted. Used for treating consecutive sequences as a single sequence."
If I understand your question correctly, then you can use map with the first argument set to None, and all the other arguments as your lists to iterate over.
E.g (from an iPython prompt, but you get the idea):
In [85]: p = [1,2,3,4]
In [86]: q = ['a','b','c','d']
In [87]: f = ['Hi', 'there', 'world', '.']
In [88]: for i,j,k in map(None, p,q,f):
....: print i,j,k
....:
....:
1 a Hi
2 b there
3 c world
4 d .
I would simply do this :
for obj in deque1 + deque2 + deque3:
some_action(obj)
>>> a = [[],[],[]]
>>> b = [[],[],[]]
>>> for c in [*a,*b]:
c.append("derp")
>>> a
[['derp'], ['derp'], ['derp']]
>>> b
[['derp'], ['derp'], ['derp']]
>>>