Aside from the possible optimizations already addressed in other answers, there are 2 important things missing from your code: documentation and input validation.
Documentation
Documentation serves 2 purposes: improving the readability of your code and communicating with future users of your code.
For the first one, it is usually good enough to rely on self-documenting code, starting with good variable names. In your case, your variable names convey little to no information, or are downright misleading: I would expect n
to be an integer, following usual mathematical conventions, when you actually expect it to be a list of integers.
The function name is also a poor fit, and although I understand you didn't choose it, I believe format_phone_number
would be much better: this function doesn't create a phone number, in fact it takes an existing phone number in some format as an input.
You can also include type hints to communicate what comes in and out of your function.
As such, a function signature would become something like:
def format_phone_number(digits: list) -> str:
Which, in itself, conveys a lot about the intent of the function.
There are still some information missing for anyone to be able to use your function: the fact that the input should be a list of exactly 10 single digits, and how the output is formatted. Note that the latter is not obvious, for example an American user would expect a 10-digit phone number to be formatted (012) 345-6789
, whereas a French user would expect 01 23 45 67 89
.
As such, you should include a docstring with your function, fully specifying your function's behavior.
Input validation
While Code Wars is probably well behaved and won't call your function with invalid parameters, you can't expect that from all users. Proper documentation will help, but nothing would prevent calling your function with unexpected parameters (something else than a list, a list with more of fewer characters than expected, a list containing out-of-range integers...). Even type hints are just hints, Python will call the function with anything the caller provides.
As such, it is good practice to verify that the arguments passed match what your logic can handle: in this case, you should verify that the argument is a list with 10 items, that these items are int
s, and that they are in range [0-9]
.
Example code
Here is my take on the exercise. I decided to use a formatted string litteral -- aka f-string -- for the actual formatting logic, as I think this is the most straightforward method, but other approaches can be equally valid, as long as the code does what it should.
def create_phone_number(digits: list) -> str:
'''
Format a phone number represented as a list of digit into a string
formatted according to the American phone numbers convention
Parameters
----------
digits : list
A list of exactly 10 digits representing a phone number
Returns
-------
str
The formatted phone number
>>> create_phone_number([1, 2, 3, 4, 5, 6, 7, 8, 9, 0])
'(123) 456-7890'
'''
if len(digits) != 10:
raise ValueError("Invalid number of digits (10 required)")
if not all(isinstance(d, int) and 0 <= d <= 9 for d in digits):
raise ValueError("'digits' must be a list of integers in range [0, 9]")
digits_as_string = ''.join([str(d) for d in digits])
return f'({digits_as_string[0:3]}) {digits_as_string[3:6]}-{digits_as_string[6:]}'
Note that I stuck to the create_phone_number
name, as following the given specifications takes priority over my own naming preferences.