Let's take it apart:
initials = '.'.join(name[0].upper() for name in userName.split())
There is an inner loop here, for name in userName.split(). This is splitting the string in userName into chunks, or names. The documentation for split() says:
Return a list of the words of the string s. If the optional second
argument sep is absent or None, the words are separated by arbitrary
strings of whitespace characters (space, tab, newline, return,
formfeed).
So if the string contains spaces, it will split the string as you would expect. "my lastname" would be split into a list ["my", "lastname"]. This list becomes the list of names in the for loop. name would become the values "my" and "lastname" in this case.
Next, we have the cryptic function name[0].upper(). This is applied to each value of name from the for loop. The first part, name[0] takes the first character of the string in name. The second part, .upper() converts that first character (which is actually a one-characters string) into an uppercase character. This is described in the same documentation as above.
Continuing our example, name[0].upper() takes the two strings "my" and "lastname", takes the first letter from each one and converts it to uppercase: "M" and "L". The resulting list is ["M", "L"].
Finally, the '.'.join() expression takes the list inside of the join() and joins them together using the '.' character. In this case, the result is "M.L".
str.join,str.upper, andstr.split.Which of those things are you unfamiliar with?