Reputation: 199
I want to conditionally replace a single line in a file, depending on some pre-conditions and write the new content. The way i choose was:
n_file = []
For line in file.getlines():
n_file.append( line.replace(
*("foo", "bar") if False else
*("baz", "bam") if True else
# ...
*("", "") # no replacement
))
new_file.write(n_file)
But i encoutered a Problem, that i dont understand.
The Problems are the asterisks. Whenever there are asterisks at the non-first position of the replace()
function, it gives me a syntax error and i have absolutely no idea why.
You can try it for yourself. Its the same in Python 2 or 3.
def tf(a,b): print( str(a) +" "+ str(b) )
tf( *(1,1) if False else (2,2) if False else (3,3) )
# prints '3 3' as expec... intended
tf( *(1,1) if False else *(2,2) if False else *(3,3) )
# invalid syntax ^
tf( (1,1) if False else (2,2) if False else (3,3) )
# missing 1 required positional argument: 'b'
Though i found the solution for my particular problem. I still dont understand why it has to be that way. My best guess is that it has something to do with the position of to-expand arguments but its still no explanation why the asteriks has to be at the first item only.
Kind regards.
ps.
This is my first question here and im not sure if the title is fitting (or how to specifically call this if-thingy, happening in replace()
-args).
The search for answers about this problem was troublesome, so if u have a better title or tag suggestions, please let me know.
Upvotes: 2
Views: 220
Reputation: 804
The use of the '*' operator inside the conditional expressions inside replace
function is the issue. The expression is invalid in this context because the * is trying to unpack within the conditional logic.
Instead, use something like
if False:
old, new = "foo", "bar"
elif True:
old, new = "baz", "bam"
else:
old, new = "", ""
Or a better cleaner version which will match your code logic.
def tf(a, b):
print(str(a) + " " + str(b))
The function has 2 parameters, a
and b
.
The function call
tf(*(1,1) if False else (2,2) if False else (3,3))
Will work because,
The expression (1,1) if False else (2,2) if False else (3,3)
will be executed first, and the result is (3,3)
.
Then the function call becomes tf(*(3,3))
which is like passing asterisk arguments to the function.
So the function definition will be matched, and the function will be executed, which is 3 3
.
tf(*(1,1) if False else *(2,2) if False else *(3,3))
This will fail because,
*(1,1) if False else *(2,2) if False else *(3,3)
is an invalid expression.tf((1,1) if False else (2,2) if False else (3,3))
This will fail because
The expression (1,1) if False else (2,2) if False else (3,3)
will evaluate to (3,3)
, which This is a tuple.
This result is a single object, whereas the function signature asks for 2 parameters to be passed.
So (3, 3)
will be passed for a
. The value for b
is missing, and it is a required positional argument, hence the error missing 1 required positional argument: 'b'
Upvotes: 0
Reputation: 78564
You need to wrap the entire cascade of ternaries with parentheses so those are evaluated first, then proceed to unpack the result of the expression. The splat operator stays outside the parens:
tf( *((1,1) if False else (2,2) if False else (3,3)) ) #-> 3 3
More specifically:
...
n_file.append( line.replace(
**(("foo", "bar") if False else
("baz", "bam") if True else
...
("", "")) # no replacement
))
OTOH, code gets quite unreadable when you're cascading ternaries like so. Would be more readable to use a vanilla if
clause, assign to a variable and then unpack the variable.
Upvotes: 2