Why isn't this syntax valid?
What can we assign to?
Python requires the left-hand side of an assignment - whether it's a plain assignment like =
, or any kind of augmented assignment like +=
- to name what will be assigned. After all, the purpose of assignment is to make it possible to refer to a value in a specific way. The rules are fairly complex, because there is a lot of flexibility; but in general, one or more "references" are required. Usually the left-hand side will be a single name (i.e., variable), but it could also be:
- recursively, an attribute of some other valid single reference (
foo.bar = 3
)
- recursively, a dict entry, element, or slice of some other valid single reference (
foo['bar'] = 3
where foo
is a dict
, or foo[0] = 3
or foo[:] = [1,2,3]
where foo
is a `list)
- an unpacking pattern (there doesn't seem to be official terminology for this, but things like
foo, bar = "hi"
, or foo, *bar = "example"
)
Function-call syntax does not qualify as any of these. Functions return values, not variables, so there is nothing to assign to. foo() = 3
makes no sense, for the same reason that 2 = 3
(or even 3 = 3
) makes no sense.
What are some common reasons for trying to write code this way?
What is the corresponding correct approach?
Here are some ways the problem could conceivably come up:
If you wanted to assign to a key in a dictionary or element in an array, use code that gives you the key or the index, not the value. See for example How do I randomly select a variable from a list, and then modify it in python?. If you wanted to assign to an attribute that was looked up with getattr
, use setattr
instead: see assign a attribute using getattr. PyQt's .text()
accessor has a corresponding .setText
mutator: see Error:Can't assign to function call Python/PyQt.
Pandas is an especially common source of this issue, because there is a lot of functionality that returns some subset of a DataFrame, and it's common to want to assign to those cells. Numpy doesn't generally have this problem because its selection is generally done using slicing, so the syntax supports assignment (Numpy arrays simply have to implement __setitem__
to accept slice
objects, and they do).
You want to use some code to compute a string, and then use the string as a variable name for an assignment. Aside from not working, this is definitely not how you want to solve the problem, whatever it is. Use a dict or a list instead. See How do I create variable variables?.
You have something like f(x) = 3
because you want to change x
in such a way that f(x) == 3
becomes true. This is not directly possible; you need to rearrange the equation. If you are simply doing math then this is just a matter of algebra. In general you will need to figure out an inverse function g
which satisfies g(f(x)) == x
for all x
values; then you can do x = g(3)
. This function may already exist. For example, see "Can't assign to function call" - chr
is the built-in inverse for ord
(and vice-versa). Archimedean spiral is a similar example with Numpy.
You want to index into a list, dict, Numpy array, etc. and set the value at that index. In Python, indexing is done with []
, not ()
. See Dictionary syntax error "can't assign to function call" or How to have an array of arrays in Python.
You have a function defined like def foo(x, y): pass
, and then you want to replace the foo
function with a different function. This is an advanced usage, but it is necessary for what is called mocking when using automated tests for your code. The mistake is in thinking that the parameter list is part of the function name, thus writing something like foo(x, y) = create_mock()
. The parameter list is not part of the name of foo
(there might be a parameter list on the right-hand side, because you are calling a function that returns a function). The code should just look like foo = create_mock()
instead. See Mocking Method Calls In Python.
You are trying to use as
in either a with
statement or an except
statement, and put ()
at the end, like with f(x) as y():
or except RuntimeError as e():
. These statements are a kind of assignment, and the parentheses are a typo - simply remove them. See with tf.Session() as session(): ^ SyntaxError: can't assign to function call.
You wanted to do some kind of pre-processing on values that you're iterating over, for example for f(x) in xs:
. This does not work because a for
loop is a kind of assignment. Instead, get the raw values like for x in xs:
, and do the preprocessing inside the loop, e.g. x = f(x)
. See How do you iterate through each email in your inbox using python?.
Similarly, you have the syntax for a list comprehension the wrong way around: [x for f(x) in xs]
. This should instead be [f(x) for x in xs]
. See Iterrows a rolling sum.
You want to enforce that the assigned value will have a specific type. int(foo) = int('3')
does not make sense; int
works on values, not on variables. (Again, it is a function, and that is how functions work.) Just make sure that the value has the appropriate type: foo = int('3')
(Of course, this is a silly example; foo = 3
is better). See SyntaxError: can't assign to function call in Python.