Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #103000 > unrolled thread
| Started by | srinivas devaki <mr.eightnoteight@gmail.com> |
|---|---|
| First post | 2016-02-16 18:16 +0530 |
| Last post | 2016-02-16 21:30 +0530 |
| Articles | 3 — 2 participants |
Back to article view | Back to comp.lang.python
Multiple Assignment a = b = c srinivas devaki <mr.eightnoteight@gmail.com> - 2016-02-16 18:16 +0530
Re: Multiple Assignment a = b = c Steven D'Aprano <steve@pearwood.info> - 2016-02-17 02:16 +1100
Re: Multiple Assignment a = b = c srinivas devaki <mr.eightnoteight@gmail.com> - 2016-02-16 21:30 +0530
| From | srinivas devaki <mr.eightnoteight@gmail.com> |
|---|---|
| Date | 2016-02-16 18:16 +0530 |
| Subject | Multiple Assignment a = b = c |
| Message-ID | <mailman.161.1455626848.22075.python-list@python.org> |
Hi,
a = b = c
as an assignment doesn't return anything, i ruled out a = b = c as
chained assignment, like a = (b = c)
SO i thought, a = b = c is resolved as
a, b = [c, c]
at-least i fixed in my mind that every assignment like operation in
python is done with references and then the references are binded to
the named variables.
like globals()['a'] = result()
but today i learned that this is not the case with great pain(7 hours
of debugging.)
class Mytest(object):
def __init__(self, a):
self.a = a
def __getitem__(self, k):
print('__getitem__', k)
return self.a[k]
def __setitem__(self, k, v):
print('__setitem__', k, v)
self.a[k] = v
roots = Mytest([0, 1, 2, 3, 4, 5, 6, 7, 8])
a = 4
roots[4] = 6
a = roots[a] = roots[roots[a]]
the above program's output is
__setitem__ 4 6
__getitem__ 4
__getitem__ 6
__setitem__ 6 6
But the output that i expected is
__setitem__ 4 6
__getitem__ 4
__getitem__ 6
__setitem__ 4 6
SO isn't it counter intuitive from all other python operations.
like how we teach on how python performs a swap operation???
I just want to get a better idea around this.
--
Regards
Srinivas Devaki
Junior (3rd yr) student at Indian School of Mines,(IIT Dhanbad)
Computer Science and Engineering Department
ph: +91 9491 383 249
telegram_id: @eightnoteight
[toc] | [next] | [standalone]
| From | Steven D'Aprano <steve@pearwood.info> |
|---|---|
| Date | 2016-02-17 02:16 +1100 |
| Message-ID | <56c33d48$0$1587$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #103000 |
On Tue, 16 Feb 2016 11:46 pm, srinivas devaki wrote:
> Hi,
>
> a = b = c
>
> as an assignment doesn't return anything, i ruled out a = b = c as
> chained assignment, like a = (b = c)
> SO i thought, a = b = c is resolved as
> a, b = [c, c]
That is one way of thinking of it. A better way would be:
a = c
b = c
except that isn't necessarily correct for complex assignments involving
attribute access or item assignment. A better way is:
_temp = c
a = _temp
b = _temp
del _temp
except the name "_temp" isn't actually used.
> at-least i fixed in my mind that every assignment like operation in
> python is done with references and then the references are binded to
> the named variables.
> like globals()['a'] = result()
That's broadly correct.
> but today i learned that this is not the case with great pain(7 hours
> of debugging.)
>
> class Mytest(object):
> def __init__(self, a):
> self.a = a
> def __getitem__(self, k):
> print('__getitem__', k)
> return self.a[k]
> def __setitem__(self, k, v):
> print('__setitem__', k, v)
> self.a[k] = v
>
> roots = Mytest([0, 1, 2, 3, 4, 5, 6, 7, 8])
> a = 4
> roots[4] = 6
> a = roots[a] = roots[roots[a]]
`roots[4] = 6` will give "__setitem__ 4 6", as you expect.
On the right hand side, you have:
roots[roots[a]]
which evaluates `roots[a]` first, giving "__getitem__ 4". That returns 6, as
you expect. So now you have `roots[6]`, which gives "__getitem__ 6", as you
expect, and returns 6.
The left hand side has:
a = roots[a] = ...
which becomes:
a = roots[a] = 6
which behaves like:
a = 6
roots[a] = 6
So you end up with:
a = roots[6] = 6
which gives "__setitem__ 6 6", **not** "__setitem__ 4 6" like you expected.
Here is a simpler demonstration:
py> L = [0, 1, 2, 3, 4, 5, 6]
py> a = L[a//100] = 500
py> print a
500
py> print L
[0, 1, 2, 3, 4, 500, 6]
Let's look at the byte-code generated by the statement:
a = L[a] = x
The exact byte-code used will depend on the version of Python you have, but
for 2.7 it looks like this:
py> from dis import dis
py> code = compile("a = L[a] = x", "", "exec")
py> dis(code)
1 0 LOAD_NAME 0 (x)
3 DUP_TOP
4 STORE_NAME 1 (a)
7 LOAD_NAME 2 (L)
10 LOAD_NAME 1 (a)
13 STORE_SUBSCR
14 LOAD_CONST 0 (None)
17 RETURN_VALUE
Translated to English:
- evaluate the expression `x` and push the result onto the stack;
- duplicate the top value on the stack;
- pop the top value off the stack and assign to name `a`;
- evaluate the name `L`, and push the result onto the stack;
- evaluate the name `a`, and push the result onto the stack;
- call setattr with the top three items from the stack.
> SO isn't it counter intuitive from all other python operations.
> like how we teach on how python performs a swap operation???
No. Let's look at the equivalent swap:
py> L = [10, 20, 30, 40, 50]
py> a = 3
py> a, L[a] = L[a], a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list assignment index out of range
This is equivalent to:
_temp1 = L[a] # 40 pushed onto the stack
_temp2 = a # 3 pushed onto the stack
a = _temp1 # 40 # rotate the stack, and pull the top item 40
L[a] = _temp2 # L[40] = 3
which obviously fails. Here's the byte-code:
py> code = compile("a, L[a] = L[a], a", "", "exec")
py> dis(code)
1 0 LOAD_NAME 0 (L)
3 LOAD_NAME 1 (a)
6 BINARY_SUBSCR
7 LOAD_NAME 1 (a)
10 ROT_TWO
11 STORE_NAME 1 (a)
14 LOAD_NAME 0 (L)
17 LOAD_NAME 1 (a)
20 STORE_SUBSCR
21 LOAD_CONST 0 (None)
24 RETURN_VALUE
If you do the swap in the other order, it works:
py> L = [10, 20, 30, 40, 50]
py> a = 3
py> L[a], a = a, L[a]
py> print a
40
py> print L
[10, 20, 30, 3, 50]
In all cases, the same rule applies:
- evaluate the right hand side from left-most to right-most, pushing the
values onto the stack;
- perform assignments on the left hand side, from left-most to right-most.
--
Steven
[toc] | [prev] | [next] | [standalone]
| From | srinivas devaki <mr.eightnoteight@gmail.com> |
|---|---|
| Date | 2016-02-16 21:30 +0530 |
| Message-ID | <mailman.176.1455638463.22075.python-list@python.org> |
| In reply to | #103012 |
On Tue, Feb 16, 2016 at 6:35 PM, Sven R. Kunze <srkunze@mail.de> wrote:
>
> First, the rhs is evaluated.
> Second, the lhs is evaluated from left to right.
Great, I will remember these two lines :)
On Tue, Feb 16, 2016 at 8:46 PM, Steven D'Aprano <steve@pearwood.info> wrote:
> _temp = c
> a = _temp
> b = _temp
> del _temp
>
>
> except the name "_temp" isn't actually used.
>
So it is like first right most expression is evaluated and then lhs is
evaluated from left to right.
> py> from dis import dis
> py> code = compile("a = L[a] = x", "", "exec")
> py> dis(code)
> 1 0 LOAD_NAME 0 (x)
> 3 DUP_TOP
> 4 STORE_NAME 1 (a)
> 7 LOAD_NAME 2 (L)
> 10 LOAD_NAME 1 (a)
> 13 STORE_SUBSCR
> 14 LOAD_CONST 0 (None)
> 17 RETURN_VALUE
>
>
> Translated to English:
>
> - evaluate the expression `x` and push the result onto the stack;
>
> - duplicate the top value on the stack;
>
> - pop the top value off the stack and assign to name `a`;
>
> - evaluate the name `L`, and push the result onto the stack;
>
> - evaluate the name `a`, and push the result onto the stack;
>
> - call setattr with the top three items from the stack.
>
thank-you so much, for explaining how to find the underlying details.
>> SO isn't it counter intuitive from all other python operations.
>> like how we teach on how python performs a swap operation???
>
> No. Let's look at the equivalent swap:
>
> In all cases, the same rule applies:
>
> - evaluate the right hand side from left-most to right-most, pushing the
> values onto the stack;
>
> - perform assignments on the left hand side, from left-most to right-most.
>
uhh, i related it with swap because I was thinking variables are
binded, like first of all for all lhs assignments get their references
or names and then put the value of rhs in them.
as `a` is a name, so the rhs reference is copied to the a
`roots[a]` is a reference to an object, so it is initialized with the
reference of rhs.
anyway I got it, and all my further doubts are cleared from that
compiled code. I tried some other examples and understood how it
works.
thanks a lot.
--
Regards
Srinivas Devaki
Junior (3rd yr) student at Indian School of Mines,(IIT Dhanbad)
Computer Science and Engineering Department
ph: +91 9491 383 249
telegram_id: @eightnoteight
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.python
csiph-web