0

update Adding a usage example to better explain why I'm trying to elicit this behavior. See update at end of post. Added better syntax for links and added a link to article suggested as an answer.

I am trying to use an iterator to alter the value of a list of variables.

>>> a = 1
>>> b = 2
>>> c = 3
>>> for i in [a,b,c]:
        i += 1
>>> a,b,c #Expecting (2, 3, 4)
(1, 2, 3)

This doesn't appear to work, I've tried some other ways (see below) without success.

  1. Will someone please tell me a better way to approach this problem?
  2. Will someone explain why the example above doesn't work as I expected?
  3. Will someone tell me how/where I could have found #2 in python help documentation?

Places I've previously looked for answers...

StackOverflow question "reassign variable to original value defined prior to the loop at start of each"

StackOverflow question on reassigning variables (but not a list of variables)

Python docs: Objects and Value Types

Python docs section 3.1.3

I feel like the last reference to the python docs might have the answer, but I was overwhelmed with the amount of info, my brain is tired, and I'm hoping someone on s.o. can help.

also tried...

>>> help(__name__)
Help on module __main__:

NAME
    __main__

DATA
    __annotations__ = {}
    a = 1
    b = 2
    c = 3
    i = 4

FILE
    (built-in)

but if anything, this only confused me further.

Lastly I tried...

>>> a = 1
>>> b = 2
>>> c = 3
>>> R = [a, b, c]
>>> for i in range(3):
    R[i] += 1

>>> a, b, c #Hoping for (2, 3, 4)
    (1, 2, 3)
>>> R #Kind of expected (1, 2, 3) based on the above behavior
    [2, 3, 4]

update I used the list for convenience, since I could iterate through its members. The part I'm not understanding is that when I say...

>>> x = [a, b, c]

I am creating a list such that

x = [the value assigned to a,
 the value assigned to b,
 the value assigned to c]

rather than

x = [the variable a, the variable b, the variable c]

so when I am trying to use the syntax

>>> x[0] += 1 #take the current ITEM in place 0 and increment its value by 1.

instead of

>>> a += 1

it is instead interpreted as... take the current VALUE of the ITEM in place 0, increment that VALUE by 1, this is the new VALUE of ITEM in place 0 ... and I lose the reference to the original ITEM... the variable a.

Here is a usage example of why I am trying to elicit this behavior...

>>> class breakfast():
>>>     def __init__(self, eggs=None, bacon=None, spam=None):
>>>        self.eggs = eggs
>>>        self.bacon = bacon
>>>        self.spam = spam
>>>        updateOrder = False
>>>        updateItems = []
>>>        for i in [self.eggs, self.bacon, self.spam]:
>>>             if i == None:
>>>                 updateOrder = True
>>>                 updateItems.append(i)
>>>             else:
>>>                 pass
>>>         if updateOrder:
>>>             self.updateOrder(itemsToUpdate = updateItems)
>>>
>>>    def updateOrder(self, itemsToUpdate):
>>>        for i in itemsToUpdate: ###also tried...
>>>     ###for i in range(len(itemsToUpdate)):
>>>            print('You can\'t order None, so I am updating your order to 0 for some of your items.')
>>>            i = 0
>>>         ###itemsToUpdate[i] = 0
>>>
>>> #Finally, the good part, behavior I'm after...
>>> myBreakfast = breakfast(eggs=2,bacon=3)

You can't order None, so I am updating your order to 0 for some of your items.
>>> myBreakfast.spam == 0 #hoping for True....
False

The only way I know would work to get this behavior is to instead say...

>>>     ...
>>>     def updateOrder(self):
>>>         orderUpdated=False
>>>         if self.eggs == None:
>>>             self.eggs = 0
>>>             orderUpdated = True
>>>         if self.bacon == None:
>>>             self.bacon = 0
>>>             orderUpdated = True
>>>         if self.spam == None:
>>>             self.spam = 0
>>>             orderUpdated = True
>>>         if orderUpdated:
>>>             print('You can\'t order None, so I am updating your order')

However, if there are (lots) more than just 3 items on the menu the code for updateOrder would become very long and worse repetitive.

1 Answer 1

1

You have to use a loop and change the each value of the list during the loop

a=[1, 2, 3]
for i in range(len(a)):
    a[i] += 1

Output will be

[2, 3, 4]

To access and change values in a list, you need to select items based on their location (you can also use slices). So, a[0] = 1, a[1] = 2 and so on. To change the value of a[0], you need to assign a new value to it (as done in the for loop).

Your example does not work because you are just changing the value of i (which is 1, then 2, then 3), instead of actually changing the list. You are not selecting any item from the list itself.

The documentation is given here (see section 3.1.3)

EDIT

Based on your clarification: Creating a list of variables that have been defined elsewhere:

a, b, c = 5, 6, 7
letters = [a, b, c]

# See id of variable a
id(a) 
# 94619952681856

# See id of letters[0]
id(letters[0]
# 94619952681856

# Add 1 to letters[0]
letters[0] += 1

# See id of letters[0]
# 94619952681888

As you can see, when the list is first created, the item in the list points to the same variable However, as soon as the value in the list is changed, python creates a new item, so that the original value is unchanged. This works the other way around also. Any change in the variable a will not affect the list, as once the variable is modified, its id will change, while the id of the item in the list will not.

And in the breakfast example, why don't you just assign 0 as the default value of each dish, instead of None? It would be a lot easier, unless there is some other reason for it.

EDIT 2: If you need to update your attributes in the way that you have given, you would need to use the setattr method

class breakfast():
    def __init__(self, eggs=None, bacon=None, spam=None):
        self.eggs = eggs
        self.bacon = bacon
        self.spam = spam
        # Create a list of attributes. This will return [self, eggs,bacon,spam]
        attributes = list(locals().keys())
        attributes.remove('self') # Remove self from the list

        updateItems = []
        for i in attributes:
            # Check if the attribute is None or not
            if getattr(self, i) == None:
                updateItems.append(i)
            else:
                pass

        for i in updateItems:
            setattr(self, i, 0) # Set the attributes that are None to 0

Sign up to request clarification or add additional context in comments.

12 Comments

I hear what you're saying and I understand that is what is happening. From 3.1.3, "Unlike strings, which are immutable, lists are a mutable type, i.e. it is possible to change their content" I am trying to have the content of the list refer to the variable, not the value assigned to the variable. updated original post with usage example.
I have edited my answer. Let me know if this is what you are looking for.
I'm using None in my program because it has a broader scope than 0. Unlike the breakfast example the item has properties of multiple types. I could use 0 in most places, but in some places a property of 0 is a valid entry. My actual program looks for invalid or missed user entered properties and prompts them to update those properties. The function id() is very helpful in understanding things, I've never encountered that function before and I will research it further.
You can look at the setattr method (I just changed the answer again). Will this work?
I've made the final edit (hopefully). Python dicts are unordered, so slicing its keys based on position is not the correct solution. So, I've made a list of all the keys, and then deleted self from that list.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.