【问题标题】:Clean and generic way to transform one element to tuple/list/set/str of one element将一个元素转换为一个元素的元组/列表/集合/字符串的简洁通用方法
【发布时间】:2021-10-24 07:12:19
【问题描述】:

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

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,) 等等。有没有干净通用的方法来做到这一点?

【问题讨论】:

    标签: python python-3.x list set tuples


    【解决方案1】:

    您可以通过以下方式近似要求:

    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])
    

    这很接近,但对于返回 ['a', 'b'] 而不是 ['ab']f(list, '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)
    

    结果是通用的,但特殊情况不能真正称为干净。

    【讨论】:

      【解决方案2】:

      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)
      

      【讨论】:

        【解决方案3】:

        你可以试试这个:

        def f(sequence_type, element):
            g = lambda *args: args
            return str(g(element)[0]) if str==sequence_type else sequence_type(g(element))
        

        【讨论】:

          【解决方案4】:

          我认为您想更改文字的行为,特别是对于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]
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2023-04-07
            • 2021-09-26
            • 2019-03-09
            • 1970-01-01
            • 2019-05-31
            • 1970-01-01
            相关资源
            最近更新 更多