我遇到一个面试问题来实现以下功能:
def f(sequence_type, element):
pass
例如:
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]'
我想出了一个朴素的方法:
def f(sequence_type, element):
return sequence_type(element)
此方法适用于str
。然而,我将得到错误,因为tupe(1)
将引发TypeError: 'int' object is not iterable
。
当然,我可以写一堆if else
来检查类型,并使用特定的方式来生成一个元素序列,例如[x]
(x,)
等等。是否有干净和通用的方法来做到这一点?
您可以这样估计需求:
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])
接近,但f(list, 'ab')
失败,返回['a', 'b']
而不是['ab']
很难理解为什么期望Python函数将长度为2的字符串与长度为1的字符串区别对待。语言本身表明list('ab') == ['a', 'b']
.
我怀疑这是从C等语言中引入的期望,这些语言将字符和字符串视为不同的数据类型,换句话说,我对问题的这一方面持保留意见。
但是说你不喜欢规范并不是成功的秘诀,所以特殊的处理必须这样编码:
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)
结果是通用的,但是特殊的大小写不能称为干净。
str
的示例并不真正符合该函数的描述,但是这里有一些通过您的测试用例的实现—您希望在转换之前首先将元素包装成tuple/list/set的集合:
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)
你可以试试这个:
def f(sequence_type, element):
g = lambda *args: args
return str(g(element)[0]) if str==sequence_type else sequence_type(g(element))
我认为您想要更改字量的行为,特别是对于str
-list
的情况。由于更改是由您决定的,这意味着您必须对if
-else
进行案例研究,或者以一些棘手的方式绕过它。在我的解决方案中,我选择后者,但我只需要str
-case的条件来规范其行为。映射是基于字符串的,应该求值。
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))
输出str a -> a
list a -> ['a']
list ab -> ['ab']
list 1 -> [1]
tuple 1 -> (1,)
set 1 -> {1}
str 1 -> 1
str [1] -> [1]