-2

Disclaimer: There are many questions asked on Stack Overflow regarding the error message SyntaxError: cannot assign to function call in Python. This question provides resources on this error and also may help reviewers find the correct duplicates. This question should not, itself, be used as the target.

The error message

SyntaxError: cannot assign to function call

is produced by all of the following examples:

foo() = 3
mydict = {}
mydict('key') = 3
for i() in [1, 2, 3]: pass
[x for int(x) in '123']
with open('file.txt') as my_file(): pass

Why is this syntax not valid, and what code should I write instead?

Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
  • "still need to be questions" That's what I'm trying to do with the "But what does that actually mean? What can we assign to, and why is syntax like foo() = 3 not valid? How can I figure out the right code to do what I want, depending on what the context (the actual foo function)?" part, and that's what I answered in my answer. I'd be happy to discuss this more on Meta. – Karl Knechtel Jun 03 '22 at 22:02
  • 2
    I don't expect the actual people who really write `foo()=3` and expect something meaningful to happen, to find their case here. – tevemadar Jun 04 '22 at 13:07
  • The question is not for **them** to find the case here. The question is for **duplicate-question voters** to find the case here. – Karl Knechtel Jun 04 '22 at 13:12
  • 2
    This asks multiple questions. Ask 1. – philipxy Jun 04 '22 at 13:46
  • 2
    @philipxy **as I have already explained multiple times**, the purpose of this question is to enable others to find the correct duplicate link for other questions. It **must** ask multiple questions because **the entire point** is that there are multiple questions that the OP might have in mind, and the goal is to find the answer that **best matches** that question. This is difficult to do from the search. Please see https://meta.stackoverflow.com/questions/418516/. – Karl Knechtel Jun 04 '22 at 13:48
  • 2
    This post is being discussed on meta: https://meta.stackoverflow.com/questions/418516/should-i-really-not-try-to-make-artifical-signpost-duplicates – philipxy Jun 04 '22 at 13:48
  • 1
    Doesn't matter. – philipxy Jun 04 '22 at 13:49
  • 5
    "enable others to find the correct duplicate link for other questions" That's not what questions are supposed to do. You can draft such a list on your computer or create a shared one like the dupe list from the Python chat room, but the main site is certainly not the place for this. – ayhan Jun 04 '22 at 14:13
  • 1
    Maybe you could ask something like, process for debugging xxx error. Although that could be considered unfocused & unresearched. That is a problem with self-answering. Asking about a bug/error should have a [mre] including explanation giving expectations & why those expectations are held justified by reference to authoritative documentation, while asking about composing the overall goal should not include misunderstood/meaningless code. But people ask bad questions instead dumping bad code & no reasoning, just asking for a rewrite of a reference/manual & bespoke tutorial that reads their mind. – philipxy Jun 04 '22 at 22:36
  • A "process" Q&A per my suggestion might best have an answer that is a branching troubleshooting flowchart, so that it is in fact one answer & doesn't ask more questions. But this Q&A becomes more & more like a [meta] or [softwareengineering.se] post. Which unfortunately it maybe needs to be because of the on-topic & voting structure of SO. Again, not surprising because the problem you are trying to solve is bad questions & they shouldn't be asked so you are trying to supply an answer/response to questions that shouldn't be asked, which doesn't really fit SO. PS Very much admire the goal. – philipxy Jun 04 '22 at 22:45

1 Answers1

3

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.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
  • Some of these links are not very good quality. You can help by editing in better reference links, or even by trying to create better-quality canonicals for the specific use cases. (I especially need help cleaning up all the Pandas examples, because I am not a Pandas expert and am not sure what common threads there are between the questions, or how to explain the problem in a generic way.) – Karl Knechtel Jun 03 '22 at 22:41
  • You can also help by going through the [search results](https://stackoverflow.com/search?tab=votes&q=%22can%27t%20assign%20to%20function%20call%22%20%5bpython%5d%20is%3aquestion) to find things that should be redirected or otherwise closed, or identifying any other patterns in the causes of the error. – Karl Knechtel Jun 03 '22 at 22:41
  • I removed (and copied locally) the Pandas links for now, pending a closer evaluation. I think I probably do have enough knowledge to go through them and figure out if there are specific patterns. In the best case, it should be possible to write a canonical explaining technique for avoiding the problem. – Karl Knechtel Jun 05 '22 at 15:40