The code
a={'k1':['a',2,3]}
b={'k2':[2,'b',4]}
for i in [a,b]:
print(i)
prints out the value of a and b. But what I actually wanted it to print the dictionary names, i.e.
a
b
My current solution is this:
a={'k1':['a',2,3]}
b={'k2':[2,'b',4]}
for i in [a,b]:
if i is a:
print('a')
elif i is b:
print('b')
Is there a better approach?
There is no way to get the name of a value. There's also never a need to.
Names refer to values. Many names can refer to the same value. Thus, there cannot be a "true name" for any given object as all names are equal.
Consider
>>> x = 5
>>> y = x
>>> x is y
True
Now, what's the name of the value 5? It does not make sense.
Consider
>>> x = [1,2,3]
>>> y = x[:] # shallow copy of x
>>> x is y
False
Now, what's the name of the list [1,2,3], which exists twice? It does not make sense.
If you are desperate to give your dictionaries a persistent name, add it as a key-value pair, e.g.
>>> mydic = {1:2, 'name':'Bob'}
>>> mydic['name']
'Bob'
But consider carefully why you actually want to do this. Chances are that you don't even want to.
Related
I have a variable, that we'll call x , which stores the string test
I have another variable, that we'll call y , which stores the string value x
Is there any way that I could print(y) , in order to return test
In other words, how can I print the value of y which itself stores the name of the variable x ?
I should also mention that I cannot just print(x) , because x changes, (as does its string value).
>>> x="test"
>>> y="x"
>>> eval(y)
'test'
But don't use eval. A better approach would be to use a mapping, such as a dictionary.
>>> mapping = {"x": "test"}
>>> mapping[y]
'test'
Actually, variables in python are in a dictionary anyway, accessible via either the globals() or locals() builtins. So, another way to access a name dynamically is…
>>> globals()[y]
'test'
…but this is just a more magical and less clear solution to creating the dictionary that you really need.
You can create a dictionary:
d = {"x": "test"}
y = "x"
print(d[y])
If you want to use variable x then you can set some value to x as initializing. As you said x value can change:
x = None # can be any value depending on the user.
d = {x: "test"}
y = x
print(d[y])
Output:
"test"
I'm not quite sure how to phrase it yet, so I'll demonstrate what I mean.
a = 1
b = 2
my_list = [a,b]
print my_list # [1,2]
a = a + 1
b = b * 2
print a # 2
print b # 4
# prints [1,2] but I want [2,4]
print my_list
This happens for tuples and sets too. What is the data structure I need? Or what is the logic I need?
Integers are immutable. That means that once you've created an integer object, e.g. with a = 1, it can never be changed. That doesn't mean that you can't rebind the name a to another object, just that the value of the integer object itself won't change.
When you do a = a + 1, you are creating a new integer with value 2, and assigning it to a. But the original 1 is still around. In fact the element my_list[0] still refers to the original object, since it was never told to refer to anything else. In that sense, names are like pointers in Python.
To get the list element to change, you have two options:
Tell it to change explicitly. Any time you modify a, set my_list[0] = a again. Or just drop a entirely. Why have two references to the same object of you don't need to? You can do
my_list[0] += 1
my_list[1] *= 2
Store a mutable object in the list. Strings and integers, as you already saw, are immutable. Tuples are as well. But sets, dicts and lists are mutable, so any in-place changes you make to them will be directly visible. This is probably not the best option in your particular case, but here's an example:
a = [1]
b = {2}
my_list = [a, b]
a[0] += 1
a.append(3)
b.add(4)
print my_list
[[2, 3], {2, 4}]
Both of methods shown here work just fine for lists. The notation for sets is a little different for sets since in the first case, you'd have to remove the original object and insert the new one. The concept is the same though.
Only the second method could work with tuples though, and even that's not usually recommended. As I mentioned earlier, tuples are supposed to be immutable. The are certain expectations that come with that, like hashability, which are violated when you put a mutable object, like a list, into a tuple. It's perfectly fine to do that, but you just have to be a little careful.
For your particular use-case (accessing the values by name, but also grouping them into a single data structure), dictionaries may be more suitable than lists. You can index a dictionary much like a list, except that the keys can be the names you want instead of essentially arbitrary indices. Your example would look like this:
my_dict = {
'a': 1,
'b': 2,
}
my_dict['a'] += 1
my_dict['b'] *= 2
print my_dict
<p/>
{'a': 2, 'b': 4}
I want a for loop in Python that can modify variables in the iterator, not just handle the value of the variables. As a trivial example, the following clearly does not do what I want because b is still a string at the end.
a = 3
b = "4"
for x in (a, b):
x = int(x)
print("b is %s" % type(b))
(Result is "b is a <class 'str'>")
What is a good design pattern for "make changes to each variable in a long list of variables"?
Short answer: you can't do that.
a = "3"
b = "4"
for x in (a, b):
x = int(x)
Variables in Python are only tags that references values. Theres is not such thing as "tags on tags". When you write x = int(x) if the above code, you only change what x points to. Not the pointed value.
What is a good design pattern for "make changes to each variable in a long list of variables"?
I'm not sure to really understand, but if you want to do things like that, maybe you should store your values not as individual variables, but as value in a dictionary, or as instance variables of an object.
my_vars = {'a': "3",
'b': "4" }
for x in my_vars:
my_vars[x] = int(my_vars[x])
print type(my_vars['b'])
Now if you're in the hackish mood:
As your variables are globals they are in fact stored as entries in a dictionary (accessible through the globals() function). So you could change them:
a = "3"
b = "4"
for x in ('a', 'b'):
globals()[x] = int(globals()[x])
print type(b)
But, as of myself, I wouldn't call that "good design pattern"...
As mentioned in another answer, there's no way to update a variable indirectly. The best you can do is assign it explicitly with unpacking:
>>> a = 3
>>> b = 4
>>> a, b = [int(x) for x in a, b]
>>> print "b is %s" % type(b)
b is <type 'int'>
If you have an actual list of variables (as opposed to a number of individual variables you want to modify), then a list comprehension will do what you want:
>>> my_values = [3, "4"]
>>> my_values = [int(value) for value in my_values]
>>> print(my_values)
[3, 4]
If you want to do more complicated processing, you can define a function and use that in the list comprehension:
>>> my_values = [3, "4"]
>>> def number_crunching(value):
... return float(value)**1.42
...
>>> my_values = [number_crunching(value) for value in my_values]
>>> print(my_values)
[4.758961394052794, 7.160200567423779]
Context: I needed to randomly erase some precise element of a few lists of numbers, extracting some random indexes and saving them in a set called aleaindex (done, it properly works, thanks to some SO users' help). Now, I'd like to substitute the old lists a, b, etc with the new, eventually shorter ones newa, newb, etc. Here is the function:
def myfunction(N, other_parameters, a, b, c):
...
while (...):
aleaindex.add(random.randint(..., ...))
...
new_a = [v for i, v in enumerate(a) if i not in aleaindex]
while a: a.pop()
a = new_a[:]
...
and so on for the other lists b, c, etc.
Problem: the function seems to correctly modify them within the module (checked by printing) but then, when I print the modified lists outside the module, that is in the "main" file, lists are as they had not modified. Where am I wrong?
This line:
a=new_a[:]
overwrites the variable a with a new object. Outside the function or module, the old object is still pointed at by a (or whatever it was called there). Try:
new_a = [v for i, v in enumerate(a) if i not in aleaindex]
while a:
a.pop()
a[:] = new_a[:]
Explanation
To see this, just try the following.
>>> a = [1,2,3,4]
>>> b = a
>>> print b
[1, 2, 3, 4]
>>> a[:] = [2,3]
>>> print b
[2, 3]
>>> a = [5]
>>> print b
[2, 3]
Example in function!
If the variable is mutable (and a normal list is), this works:
>>> def f(a):
... a[0] = 2
>>> b = [3]
>>> f(b)
>>> print b
[2]
Variables are not passed by value - you can edit a mutable value.
I do not know what you are trying to do but from your snippets you are clearly lost. Your code does not make much sense and there are more more than one problem. Nonetheless, the problem you asked about - why the list is not fully changed? - seems to be related to this loop:
while a: a.pop()
a = new_a[:]
Suppose we call your function this way:
list1 = [1, 2, 3, 4, 5, 6, 7]
myfunction(N, other_parameters, list1, [], [])
What will happen is, when you call the first line, you will get a variable called list1 and it will point to a list:
When you call the function myfunction(), the function, among other things, create a variable called a which will point to the same list pointed by list1:
So far, so good. Then we get at the loop below:
while a:
a.pop()
a = new_a[:]
In the first line of it (a.pop()), you get an item out of the list. Since both variables a and list1 points to the same list, you would see the same result...
...if it were not for the next line of the loop (a = new_a[:]). In this line, you are making the a variable to point to another list:
Now, every operation you execute on a will be in this list, which is in no way related to list1. For example, you can execute a.pop() at the next iteration to get it:
However, it makes no sense at all, because the line a = new_a[:] will replace the list pointed to a again for yet another different list:
So, what is the solution? I don't know. As I have said, it is not possible (to me, at least) to make sense from your code. You have to reflect a bit more about what you are trying to do and explain it to us, with a bit more of context.
There is no function in the code you have posted. I suspect the problem is that you are not returning the new value.
Your code likely does something like:
a = "foo"
def func():
a = "bar" # uh-oh this not the same as the global a
func()
At this point global a is unchanged because the a local to func is not the same variable.
You want to do:
a = "foo"
def func():
return "bar"
a = func()
That code assigns to the a in global scope, changing it.
I am working with Python list sort.
I have two lists: one is a list of integers, the other is a list of objects, and the second object list has the attribute id which is also an integer, I want to sort the object list based on the id attribute, in the order of the same id appears in the first list, well, this is an example:
I got a = [1,2,3,4,5]
and b = [o,p,q,r,s], where o.id = 2, p.id = 1, q.id = 3, r.id = 5, s.id = 4
and I want my list b to be sorted in the order of its id appears in list a, which is like this:
sorted_b = [p, o, q, s, r]
Of course, I can achieve this by using nested loops:
sorted_b = []
for i in a:
for j in b:
if j.id == i:
sorted_b.append(j)
break
but this is a classic ugly and non-Python way to solve a problem, I wonder if there is a way to solve this in a rather neat way, like using the sort method, but I don't know how.
>>> from collections import namedtuple
>>> Foo = namedtuple('Foo', 'name id') # this represents your class with id attribute
>>> a = [1,2,3,4,5]
>>> b = [Foo(name='o', id=2), Foo(name='p', id=1), Foo(name='q', id=3), Foo(name='r', id=5), Foo(name='s', id=4)]
>>> sorted(b, key=lambda x: a.index(x.id))
[Foo(name='p', id=1), Foo(name='o', id=2), Foo(name='q', id=3), Foo(name='s', id=4), Foo(name='r', id=5)]
This is a simple way to do it:
# Create a dictionary that maps from an ID to the corresponding object
object_by_id = dict((x.id, x) for x in b)
sorted_b = [object_by_id[i] for i in a]
If the list gets big, it's probably the fastest way, too.
You can do it with a list comprehension, but in general is it the same.
sorted_b = [ y for x in a for y in b if y.id == x ]
There is a sorted function in Python. It takes optional keyword argument cmp. You can pass there your customized function for sorting.
cmp definition from the docs:
custom comparison should return a negative, zero or positive number depending on whether the first argument is considered smaller than, equal to, or larger than the second argument
a = [1,2,3,4,5]
def compare(el1, el2):
if a.index(el1.id) < a.index(el2.id): return -1
if a.index(el1.id) > a.index(el2.id): return 1
return 0
sorted(b, cmp=compare)
This is more straightforward however I would encourage you to use the key argument as jamylak described in his answer, because it's more pythonic and in Python 3 the cmp is not longer supported.