将一个元素转换为一个元素的tuple/list/set/str的干净通用方法



我遇到一个面试问题来实现以下功能:

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]

相关内容

最新更新