You can use exception handling to do this, but as you say, it's not very elegant. Also, it's not so efficient if the exception gets raised often, it's faster to use an explicit test.
Actually, it would be even more elegant to redesign your code. ;) Just because Python allows you to define a function where the arg can be a number or a list that doesn't mean it's a Good Idea. As you've discovered it tends to make the internal logic of the function more complicated, and it often leads to duplicated code in the different execution paths. But anyway...
The clean way to test the arg is to see whether or not it's an iterable, and you can use the collections.Iterable
class to do that. In modern versions of Python, that class has been moved to the collections.abc
module, but it's currently still available in collections
, to make it easier to write code that runs correctly on Python 2 & Python 3.
BTW, it's generally not a good idea to use "naked" except
. You should always name the exception(s) that you want to catch, otherwise you may end up catching things that you don't expect, and that your code won't handle correctly.
Also, it's a good idea to follow the PEP-0008 style guide. It makes it easier for others to read your code.
Here's a more compact version of your function, written using PEP-0008 style names. It uses itertools.cycle
to simplify cycling over the source data. It also gathers the output strings into a list & joins them together in a single step. This is more efficient than doing string concatenation in a loop. This code runs correctly on both Python 2 and Python 3.
from __future__ import print_function
from collections import Iterable
from itertools import cycle
def build_pattern(source, framework):
if not isinstance(source, Iterable):
source = [source]
source = cycle(map(str, source))
final_pattern = ['-->']
for char in framework:
final_pattern.append(next(source) if char == 'x' else ' ')
return ' '.join(final_pattern)
print(build_pattern(3, 'x x x x x x x x ')) #single number call
print(build_pattern([1, 3, 5], 'x x x x x x x x ')) #list call
print(build_pattern('abcde', 'x x x x x x x x ')) #string call
output
--> 3 3 3 3 3 3 3 3
--> 1 3 5 1 3 5 1 3
--> a b c d e a b c
As VPfB mentions in the comments, a string is an iterable, so if you pass a string to my build_pattern
it will get passed directly to cycle(map(str, source))
, it won't get wrapped in a list. That's quite ok here, but there are situations where this behaviour can cause problems, the classic case being the flattening of an arbitrarily-nested list. The answers here show how to handle that situation.