Some of the confusion here may be the difference between the format() function and the related "string".format() method. The function invokes the object's (first argument) __format__() method with a format specification (second argument). If the second argument is None, then it's just like str(). The format() method of str walks the string on which it is invoked, looking for substitution brackets which may, or may not contain format specifications to be applied similar to the format() function. An example:
>>> # format() function which invokes float.__format__()
>>> format(0.324, "+.4%")
'+32.4000%'
>>>
>>> # str format() method that invokes various object.__format__() methods
>>> # if a format specification is found in the substitution brackets:
>>> "{:+.4%} rise in {}".format(0.324, "temperature")
'+32.4000% rise in temperature'
>>>
Question : How did format() worked without {} and . operator
It worked because without the '.' operator, you mistakenly invoked the format() function which, with no second argument, simply return the string. The format() function doesn't use {} brackets. It's the format() method of str, invoked by the '.' operator, that uses {} brackets to make substitutions, some of which may require formatting.
Perhaps it might have been less confusing if the str method had a different name like "{} string {}".interpolate(x, y) as formatting is only part of what it does.
days) and format it with a string. Days isn't a dictionary or array.