0

Writing a very simply function to mask all but the last 4 digits of a string with "#" characters. This is what I have so far:

def maskify(cc):
    res = "#" * (len(cc) - 4) if len(cc) > 4 else return cc
    res += cc[len(cc) - 4:]
    return res    

print(maskify("12355644"))

If I write out the ifs as statements, instead of using them as ternary operators, the function works fine. If I try to do the above, I get an invalid syntax error on res = "#" * (len(cc) - 4) if len(cc) > 4 else return cc The carat is pointing to the n in return. If I rewrite the above line to exclude the else part, then the carat points to the > 4.

What am I missing here? The program works fine using a traditional if-else method, but with the ternary all I'm seeing is an expression. Replacing len(cc) with a variable doesn't change anything either.

4
  • 4
    You can't have a return statement in a conditional expression Commented Aug 28, 2015 at 15:07
  • You can, however, return x if cond else y if you can figure out how to rewrite your function as such. Commented Aug 28, 2015 at 15:08
  • What do you expect the return to do there? return is not an expression, and cannot be made part of other expressions. Commented Aug 28, 2015 at 15:09
  • Would this be used in a security context? If so if cc is less than 4 characters perhaps all characters should be masked otherwise the whole value would be known. Commented Aug 28, 2015 at 15:28

2 Answers 2

3

You don't need a ternary expression at all here, just slice and use the length minus 4 times "#' to generate the prefix:

def maskify(cc):
    return "#" * (len(cc) - 4) + cc[-4:]

If the len(cc) - 4 value is 0 or smaller the multiplication produces an empty string.

Demo:

>>> def maskify(cc):
...     return "#" * (len(cc) - 4) + cc[-4:]
... 
>>> maskify("12355644")
'####5644'
>>> maskify("355644")
'##5644'
>>> maskify("5644")
'5644'
>>> maskify("44")
'44'

Your syntax error stems from your use of return inside an expression. return is a statement, and you cannot use statements in expressions. Statements have places where expressions fit in, not the other way around.

If you need to return when a condition is met, you have no option but to use statements (if followed by return):

if len(cc) < 4:
    return cc
res = "#" * (len(cc) - 4) 
res += cc[-4:]
return res

but the if test is not really needed.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks, I didn't know if the slice would work correctly if the string was less than 4 characters long.
@jktstance: slicing always produces the same type of object, meaning you cannot go out of bounds, only produce an empty result.
On closer look, it seems that a ternary if operator NEEDS the else, so that is why rewriting the above to exclude the else also fails. The ternary is applying to the right side of the assignment, not the whole res = "#" * (len(cc) -4) phrase. That makes sense now, as a missing else would mean res is unknown if len(cc) <= 4. Thanks for the help!
1

The following solution makes the assumption that this would have a security type use, as such strings of 4 or fewer characters should just be hashed, otherwise someone would know the whole string.

import string

def maskify(cc):
    if len(cc) < 9:
        split = [0,1,2,3,4,4,4,4,4][len(cc)]
    else:
        split = len(cc) - 4

    return "#" * split + cc[split:]

for length in range(1,12):
    test = string.ascii_lowercase[:length]
    print("%s > %s" % (test, maskify(test)))

Giving the following results:

a > #
ab > ##
abc > ###
abcd > ####
abcde > ####e
abcdef > ####ef
abcdefg > ####efg
abcdefgh > ####efgh
abcdefghi > #####fghi
abcdefghij > ######ghij
abcdefghijk > #######hijk

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.