0

From this answer: How do chained assignments work?, I understand that chained assignement in Python :

x = y = z      # (1)

is equivalent to:

temp = z
x = temp
y = temp

But is (1) also equivalent to:

x = z
y = x

?

Or is there a slight difference (for example when z = some_function())? If so, which difference?

Basj
  • 41,386
  • 99
  • 383
  • 673

3 Answers3

1

In the very example you give, yes, the effects of the two approaches are practically identical because both involve simply assigning the same reference to a number of names.

Be aware, however, that if the expressions in the assignment targets involve more complex evaluations, the two approaches could be different.

For example, consider the following chain expression, where x is initialized as a dict and expensive_func is a time-consuming function that returns a key:

x[expensive_func()] = y = some_function()

While it would be indeed equivalent to the following:

temp = some_function()
x[expensive_func()] = temp
y = temp

it would not be be equivalent to the second approach:

x[expensive_func()] = some_function()
y = x[expensive_func()]

since expensive_func would then have to be called twice, doubling the time taken, and triggering the side effect of the function twice, if it has any.

Also, consider the following code:

obj = []
x = []
x[:] = y = obj
print(id(obj), id(x), id(y))

where the output would show that y gets assigned the same reference as obj, while x is different.

That code is then indeed equivalent to:

obj = []
x = []
temp = obj
x[:] = temp
y = temp
print(id(obj), id(x), id(y))

But not equivalent to:

obj = []
x = []
x[:] = obj
y = x[:]
print(id(obj), id(x), id(y))

The latter of which would show y getting a different reference from both obj and x.

blhsing
  • 91,368
  • 6
  • 71
  • 106
  • I've updated my answer to make the conclusion clearer then. – blhsing Apr 15 '20 at 17:17
  • why is it not identical, your case tests one scenario? – de_classified Apr 15 '20 at 17:18
  • 1
    Oops I indeed misunderstood the question and thought it was asking about "Are x = y = z and y = x = z equivalent?" I've updated my answer accordingly then. – blhsing Apr 15 '20 at 17:55
  • 1
    Thank you for these examples, with `id`, very insightful. – Basj Apr 15 '20 at 19:00
  • I also discovered that each time we call `x[:]`, it generates a *new* copy, is that right? `x = []; y = x[:]; z = x[:]; print(id(x), id(y), id(z))` : 3 different references! – Basj Apr 15 '20 at 19:17
  • That's right. Slicing a list creates a new copy unless the slicing is done in an assignment target, in which case the assignment replaces the values of the slice in-place, which is what would happen in `x[:] = z`. – blhsing Apr 15 '20 at 19:22
1

I always find using examples to be the best way to understand things (in general).

Let's say we have a func:

def function_sample ():
  print(20)

If you print it:

print(function_sample)

<function function_sample at 0x7f8f840a01f0>

returns the function object.

When assigning to a variable a function without parentheses (without calling/running it).

x = function_sample
print(x)

you will get the same message: <function function_sample at 0x7f8f840a01f0>

However, if you run it (with parentheses).

print(x())

You will see :

20
None

Why None? It's because Python functions have a default return value, which is None if no return expression is given, or return is given on its own.

Another sample:

def another_sample(some):
  print(some)

y = another_sample
print(y)

As you probably have guessed it : <function another_sample at 0x7f8f7e747700>

If you try to print y() you will get an error because the some argument is missing.

But if we add one:

print(y(5))

5
None

One last example:

def third_sample ():
  return 20

aa = third_sample    # without running the func
bb = third_sample()  # calling/running the func

print(aa) # function object
print(bb) # 20
andreis11
  • 1,133
  • 1
  • 6
  • 10
  • Thank you for your post, but the question is not "What is the difference between x and x()?" ;) – Basj Apr 15 '20 at 17:40
0

The 2 approaches you have shown are both functional and legit in terms of going about chaining and using previous variables. No difference at all

When assigning variables to the same number of variables, instead of doing the typical:

x = 0
y = 0

OR using tuple unpacking approach:

(x,y) = 0,0

You could just do like what you have (chained assignment):

x = y = 0

This could be used with any object (being called on) for the RHS, and that:

x = y = some_object() 

is the same as:

tmp = some_object()
x = tmp
y = tmp

and when you del tmp, the xand y become useless or nothing.

de_classified
  • 1,927
  • 1
  • 15
  • 19
  • Your post is interesting but does not really answer the question: what is the difference between the two solutions mentioned in the question? Is it identical or not? – Basj Apr 15 '20 at 17:11
  • 1
    Yup, they are identical, the way you have it seems right, it's always a good idea to do some debugging on ide to reassure your cases are right. – de_classified Apr 15 '20 at 17:17
  • I know that `x=y=z` <=> `tmp=z; x=tmp; y=tmp`. The question here is: is it also strictly equivalent to `x=z; y=z`? – Basj Apr 15 '20 at 17:44