Why Does Python Assignment Not Return a Value

Why does Python assignment not return a value?

There are many who feel that having assignments be expressions, especially in languages like Python where any value is allowable in a condition (not just values of some boolean type), is error-prone. Presumably Guido is/was among those who feel that way. The classic error is:

if x = y: # oops! meant to say ==

The situation is also a bit more complicated in Python than it is in a language like C, since in Python the first assignment to a variable is also its declaration. For example:

def f():
print x

def g():
x = h()
print x

In these two functions the "print x" lines do different things: one refers to the global variable x, and the other refers to the local variable x. The x in g is local because of the assignment. This could be even more confusing (than it already is) if it was possible to bury the assignment inside some larger expression/statement.

if python assignments don't return a value how can we do a = b = c = 42

Because of a special exception in the syntax, carved out for that exact use case. See the BNF:

assignment_stmt ::=  (target_list "=")+ (expression_list | yield_expression)

Note the (target_list "=")+.

In python, return value only when the function is used in an assignment

You can't and should not worry about this. Use different functions or have your function take an argument that controls what is returned, if this is an issue.

Assignments are wide and varied and can include assignments that are explicitly discarded again. For example, if your function was used in a comparison expression:

if f() == 'Return something':

there is no assignment, but the return value matters.

Using different functions:

def f_with_return():
return 'something'

def f_without_return():
f_with_return() # ignores the return value!
print "I won't return something"

or parameters:

def f(return_something=True):
if return_something:
return 'something'
print "I won't return something"

lets you control what is returned.

However, if you don't explicitly return anything, Python still returns a value: None.

My function not returning any result in python?

Replace

getFactorPairs(120) # This doesn't give any result. Why?

with

temp = getFactorPairs(120)
print(temp)

The reason

When a function return value/values it is not automatically displayed by python.

What does the operator += return in Python

The semantics of Python might be confusing matters. As an illustrative example, try working through the following example:

class Foo:
def __init__(self, value):
self.value = value

def __iadd__(self, other):
self.value = self.value + other.value
return 'did iadd'

a = Foo(1)
c = a
c += Foo(2)

print((c, a.value))

should result in ('did iadd', 3) being printed

As per the docs:

x += y is equivalent to x = x.__iadd__(y)

The contract for __iadd__ is that you should implement it in such a way that the semantics of x = x + y evaluates to the "same" as x += y. The "same" depends on the details of the problem, e.g. for immutable objects (such as integers or strings) this will necessitate allocating a new object and updating the reference to point to, e.g.:

a = 500  # move out of the "interned" number space
c = a
a += 1
print(id(a), id(c))

should give you two different numbers, which in CPython will be the addresses of the two int objects, both distinct and immutable. For other data types, it can be useful to allow mutable/inplace updates to occur, e.g. for efficiency or other reasons. The semantics of __iadd__ allow for the class implementation to choose a default.

Each language has its own peculiarities, hence I'd suggest not trying to compare it too literally to C++. Especially as C++ has a lot of historical baggage it's being forced to carry along.

Assignment statement value

It's right there in the syntax:

assignment_stmt ::=  (target_list "=")+ (expression_list | yield_expression)

The tiny + at the end of (target_list "=")+ means "one or more". So the line a = b = c = 2 does not consist of 3 assignment statements, but of a single assignment statement with 3 target lists.

Each target list in turn consist only of a single target (an identifier in this case).

It's also in the text (emphasis mine):

An assignment statement [...] assigns the single resulting object to each of the target lists, from left to right.

This can lead to interesting results:

>>> (a,b) = c = (1,2)
>>> (a, b, c)
(1, 2, (1, 2))

How does python assign values after assignment operator

I've faced this problem before and understand that it gets confusing. There are two concepts here:

  1. some data structures are mutable, while others are not
  2. Python works off pointers... most of the time

So let's consider the case of a list (you accidentally stumbled on interning and peephole optimizations when you used ints - I'll get to that later)

So let's create two identical lists (remember lists are mutable)

In [42]: a = [1,2]

In [43]: b = [1,2]

In [44]: id(a) == id(b)
Out[44]: False

In [45]: a is b
Out[45]: False

See, despite the fact that the lists are identical, a and b are different memory locations. Now, this is because python computes [1,2], assigns it to a memory location, and then calls that location a (or b). It would take quite a long time for python to check every allocated memory location to see if [1,2] already exists, to assign b to the same memory location as a.

And that's not to mention that lists are mutable, i.e. you can do the following:

In [46]: a = [1,2]

In [47]: id(a)
Out[47]: 4421968008

In [48]: a.append(3)

In [49]: a
Out[49]: [1, 2, 3]

In [50]: id(a)
Out[50]: 4421968008

See that? The value that a holds has changed, but the memory location has not. Now, what if a bunch of other variable names were assigned to the same memory location?! they would be changed as well, which would be a flaw with the language. In order to fix this, python would have to copy over the entire list into a new memory location, just because I wanted to change the value of a

This is true even of empty lists:

In [51]: a = []

In [52]: b = []

In [53]: a is b
Out[53]: False

In [54]: id(a) == id(b)
Out[54]: False

Now, let's talk about that stuff I said about pointers:

Let's say you want two variables to actually talk about the same memory location. Then, you could assign your second variable to your first:

In [55]: a = [1,2,3,4]

In [56]: b = a

In [57]: id(a) == id(b)
Out[57]: True

In [58]: a is b
Out[58]: True

In [59]: a[0]
Out[59]: 1

In [60]: b[0]
Out[60]: 1

In [61]: a
Out[61]: [1, 2, 3, 4]

In [62]: b
Out[62]: [1, 2, 3, 4]

In [63]: a.append(5)

In [64]: a
Out[64]: [1, 2, 3, 4, 5]

In [65]: b
Out[65]: [1, 2, 3, 4, 5]

In [66]: a is b
Out[66]: True

In [67]: id(a) == id(b)
Out[67]: True

In [68]: b.append(6)

In [69]: a
Out[69]: [1, 2, 3, 4, 5, 6]

In [70]: b
Out[70]: [1, 2, 3, 4, 5, 6]

In [71]: a is b
Out[71]: True

In [72]: id(a) == id(b)
Out[72]: True

Look what happened there! a and b are both assigned to the same memory location. Therefore, any changes you make to one, will be reflected on the other.

Lastly, let's talk briefly about that peephole stuff I mentioned before. Python tries to save space. So, it loads a few small things into memory when it starts up (small integers, for example). As a result, when you assign a variable to a small integer (like 5), python doesn't have to compute 5 before assigning the value to a memory location, and assigning a variable name to it (unlike it did in the case of your lists). Since it already knows what 5 is, and has it stashed away in some memory location, all it does is assign that memory location a variable name. However, for much larger integers, this is no longer the case:

In [73]: a = 5

In [74]: b = 5

In [75]: id(a) == id(b)
Out[75]: True

In [76]: a is b
Out[76]: True

In [77]: a = 1000000000

In [78]: b = 1000000000

In [79]: id(a) == id(b)
Out[79]: False

In [80]: a is b
Out[80]: False

Using assignment as operator

You should do some more research about the differences between statements and expressions in Python.

If you are using Python 3.8+, you can use the := operator:

In [1]: class A:
...: def save(self):
...: return 1
...:

In [2]: (a := A()).save()
Out[2]: 1

In [3]: a
Out[3]: <__main__.A at 0x7f074e2ddaf0>


Related Topics



Leave a reply



Submit