I meet a interview question to implement following function:
def f(sequence_type, element):
pass
for example:
f(str, 'a') return 'a'
f(list, 'a') return ['a']
f(list, 'ab') return ['ab']
f(list, 1) return [1]
f(tuple, 1) return (1, )
f(set, 1) return {1}
f(str, 1) return '1'
f(str, [1]) return '[1]'
I came up a naive method:
def f(sequence_type, element):
return sequence_type(element)
This method works for str
. However, I will get error, since tupe(1)
will raise TypeError: 'int' object is not iterable
.
Certainly, I can write bunch of if else
to check type and use specific way to generate one element sequence e.g. [x]
(x,)
and so on. Is there clean and generic way to do this?
CodePudding user response:
You can approximate the requirement this way:
from collections.abc import Iterable
def f2(sequence_type, *elements):
if isinstance(elements[0],Iterable):
return sequence_type(elements[0])
else:
return sequence_type(elements[:1])
which is close, but fails for f(list, 'ab')
which returns ['a', 'b']
not ['ab']
It is hard to see why one would expect a Python function to treat strings of length 2 differently from strings of length 1. The language itself says that list('ab') == ['a', 'b']
.
I suspect that is an expectation imported from languages like C that treat characters and strings as different datatypes, in other words I have reservations about that aspect of the question.
But saying you don't like the spec isn't a recipe for success, so that special treatment has to be coded as such:
def f(sequence_type, elements):
if isinstance(elements, str) and len(elements) > 1 and sequence_type != str:
return sequence_type([elements])
else:
return f2(sequence_type, elements)
The result is generic but the special-casing can't really be called clean.
CodePudding user response:
The examples for str
doesn't really fit the description of the function, but here are some implementations that pass your test cases - you want to wrap the element into a collection first for tuple/list/set before converting:
def f(sequence_type, element):
return sequence_type([element] if sequence_type != str else element)
def f(sequence_type, element):
return sequence_type((element,) if sequence_type != str else element)
def f(sequence_type, element):
return sequence_type({element} if sequence_type != str else element)
CodePudding user response:
You can try this one:
def f(sequence_type, element):
g = lambda *args: args
return str(g(element)[0]) if str==sequence_type else sequence_type(g(element))
CodePudding user response:
I think that you want to change the behavior of the literals, in particular for the case str
-list
. Since the changes are decided by you it means that you have to make either a case-study with if
-else
or by bypassing it in some tricky way. In my solution I choose the latter but I required only a conditional for the str
-case to normalized its behavior. The mapping is string-based that should be evaluated.
def f(sequence_type, element):
type_literal_map = {tuple: '({},)', set: '{{{}}}', list: '[{}]', str: '"{}"'}
if isinstance(element, str) and not isinstance(sequence_type(), str):
element = f'"{element}"'
return eval(type_literal_map[sequence_type].format(element))
l = [(str, 'a'), (list, 'a'), (list, 'ab'), (list, 1), (tuple, 1), (set, 1), (str, 1), (str, [1])]
for t in l:
print(t[0].__name__, t[1],'->', f(*t))
Output
str a -> a
list a -> ['a']
list ab -> ['ab']
list 1 -> [1]
tuple 1 -> (1,)
set 1 -> {1}
str 1 -> 1
str [1] -> [1]