【问题标题】:How do you generalise the creation of a list with many variables and conditions of `if`?您如何概括创建具有许多变量和条件的“if”列表?
【发布时间】:2018-06-16 20:05:55
【问题描述】:

我创建一个列表如下:

['v0' if x%4==0 else 'v1' if x%4==1 else 'v2' if x%4==2 else 'v3' for x in list_1]

如何泛化这样一个列表的创建,使其可以很容易地被更大数量的变量和后续条件扩展?

【问题讨论】:

    标签: python list if-statement


    【解决方案1】:

    这是我对通用解决方案的尝试。一、设置——

    list_1 = [1, 2, 4, 5, 10, 4, 3]
    

    前两个选项基于纯 python,而后两个使用数字库(numpy 和 pandas)。


    dict.get

    生成一个mapping 的键值。在列表推导中,查询dict.get -

    mapping = {0 : 'v0', 1 : 'v1', 2 : 'v2'}
    r = [mapping.get(x % 4, 'v3') for x in list_1]
    

    r
    ['v1', 'v2', 'v0', 'v1', 'v2', 'v0', 'v3']
    

    这里,'v3' 是当x % 4 的结果不作为mapping 中的键存在时返回的默认值。

    这适用于任何组任意条件和值,而不仅仅是问题中概述的条件(模算术)。


    collections.defaultdict

    使用defaultdict 可以实现类似的解决方案 -

    from collections import defaultdict
    
    mapping = defaultdict(lambda: 'v3', {0: 'v0', 1: 'v1', 2: 'v2', 3: 'v3'})
    r = [mapping[x % 4] for x in list_1]
    

    r
    ['v1', 'v2', 'v0', 'v1', 'v2', 'v0', 'v3']
    

    这类似于 选项 1


    numpy.char.add

    如果您使用numpy,那么您可能对涉及模算术和广播加法的矢量化解决方案感兴趣 -

    r = np.char.add('v', (np.array(list_1) % 4).astype('<U8'))
    

    r
    array(['v1', 'v2', 'v0', 'v1', 'v2', 'v0', 'v3'],
          dtype='<U9')
    

    如果您需要一个列表作为最终结果,您可以致电r.tolist()。请注意,此解决方案针对您的特定用例进行了优化。使用numpy 使用np.where/np.select 可以实现更通用的方法。


    pd.Series.mod + pd.Series.radd

    类似的解决方案也适用于pandas mod + radd -

    r = pd.Series(list_1).mod(4).astype(str).radd('v')
    r
    
    0    v1
    1    v2
    2    v0
    3    v1
    4    v2
    5    v0
    6    v3
    dtype: object
    

    r.tolist()
    ['v1', 'v2', 'v0', 'v1', 'v2', 'v0', 'v3']
    

    【讨论】:

    • @WillemVanOnsem 谢谢。我的回答旨在解决适用于任何一组条件和值的一般情况。我想我并不清楚这一点,并且反对者没有接受它。
    • Nitpick:基于字典的方法将为列表中的每个匹配项分配相同的实例,而 OP 的原始代码每次都会创建一个新实例。很可能这根本不是问题(当然不是字符串或数字),但在某些情况下可能是。只是想指出这一点。
    • @jezrael 哇……太可怕了……你是怎么发现的??
    【解决方案2】:
    def condition(rule, out):
      return lambda x: out(x) if rule(x) else None
    
    def rule1(x): return x%4 == 0
    def out1(x): return 'v0'
    
    def rule2(x): return x%4 == 1
    def out2(x): return 'v1'
    
    def rule3(x): return x%4 == 2
    def out3(x): return 'v2'
    
    lastrule = lambda x: True
    lastout = lambda x: 'v3'
    
    check1 = condition(rule1, out1)
    check2 = condition(rule2, out2)
    check3 = condition(rule3, out3)
    check_last = condition(lastrule, lastout)
    
    def tranform(*check_list):
      def trans_value(x):
        for trans in check_list:
          if trans(x) is not None:
            return trans(x)
      return trans_value
    
    list_1=[4,5,6,7,8]      
    print([tranform(check1, check2, check3, check_last)(x) for x in list_1])
    

    对于长检查,首先形成一个条件列表可能更容易。假设条件公式和输出都是x的函数,没有其他输入参数。下面的方法节省了一些输入,同时保持了长时间检查的可加性。

    为了实现更通用的方法(更复杂的条件,多个参数),一些复合程序可能会有所帮助(比如both(greater, either(smaller, identity)),整个程序需要重新重组,这意味着程序的可加性还没有理想,因为它还不够通用。

    outconstants = ['v0', 'v1', 'v2', 'v3']
    # for this specific example. In general, only outf is needed (see below)
    n = len(outconstant)
    
    outf = lambda out: lambda x: out
    outs = [outf(out) for out in outconstants]
    # define your own outf formula, if not output constant
    # define multiple formulas and put into list, if different type of outputs are needed
    
    rights = map(lambda constant: lambda x: constant, range(n-1)) 
    lefts = [lambda x: x%4 for _ in range(n-1)]
    # right and left formulas can be also defined separately and then put into list
    
    def identity(a, b): return lambda x: a(x) == b(x) 
    # define other rules if needed and form them into rules list with proper orders
    # e.g., def greater(a, b): return lambda x: a(x) > b(x), ...
    
    
    lastrule=lambda x: True
    
    rules = list(map(identity, lefts, rights))
    rules.append(lastrule)
    # in complex case, each unique rule needs to be defined separately and put into list
    # if new rule is needed, define it and append here before lastrule (additive)
    
    def transform(rules, outs):
        def trans_value(x):
            for rule, out in zip(rules, outs):
                if rule(x):
                    return out(x)
        return trans_value
    
    list_1=[4,5,6,7,8]
    print([transform(rules, outs)(x) for x in list_1])
    

    【讨论】:

      【解决方案3】:

      在给定的示例中,很明显我们可以“压缩”条件,从而得出此处给出的具体解决方案。但在一般情况下,我们不能假设有一些“技巧”可以在一行中快速写出所有可能的条件。

      我会在函数中写出所有条件:

      def conditions(x):
          if x == <option a>:
              return <result a>
          elif x == <option b>:
              return <result b>
          .
          .
          .
          else:
              return <default option>
      

      如果您只是使用比较操作,则可以只使用collections.defaultdict,如其他回复所示。如果条件更复杂,那么您可能不得不写出如图所示的整个函数。

      现在你列出理解,你可以这样做:

      values = [conditions(x) for x in my_list_of_values]
      

      【讨论】:

        【解决方案4】:

        字符串格式

        为什么不在这里使用模运算,并进行字符串格式化,例如:

        ['v{}'.format(x%4) for x in list_1]
        

        因此,我们在这里计算x%4,并将其附加到字符串中的'v'。好消息是我们可以轻松地将4 更改为另一个数字。

        元组或列表索引

        如果输出字符串不遵循这样的结构,我们可以构造一个列表或元组来保存值。喜欢:

        # in case the values do not follow a certain structure
        vals = ('v0', 'v1', 'v2', 'v3')
        [vals[x%4] for x in list_1]
        

        通过以这种方式对其进行索引,可以清楚地知道哪些值将映射到哪个索引。这很好用,考虑到操作的结果——这里是x%4——映射到一个n(有一个合理的小n)。

        (默认)字典

        如果操作没有映射到 n,但仍然映射到有限数量的 hashable 项,我们可以使用字典。例如:

        d = {0: 'v0', 1: 'v1', 2: 'v2', 3: 'v3'}
        

        或者如果我们想要一个“后备”值,则在查找失败时使用的值:

        from collections import defaultdict
        
        d = defaultdict(lambda: 1234, {0: 'v0', 1: 'v1', 2: 'v2', 3: 'v3'})
        

        这里1234作为一个备用值,然后我们可以使用:

        [d[x%4] for x in list_1]
        

        使用d[x%4] 而不是d.get(x%4) 给定d 是一个字典,如果我们想防止失败的查找被忽视,它会更有用。在这种情况下会出错。尽管错误通常不是一个好兆头,但在查找失败的情况下引发错误比添加默认值更好,因为这可能是某些事情无法正常工作的征兆。

        【讨论】:

        • 这个答案是对模运算的超特定。当 OP 有一些任意操作和任意值要返回时,一般情况会怎样?
        • @cᴏʟᴅsᴘᴇᴇᴅ:它适用于所有映射在 Zn 上的东西。我认为使用它的好处是我们确信我们不会忘记一个值。如果我们使用.get(..),那么一个潜在的问题是错误通过静默,这与Python的哲学背道而驰。
        • 我同意,但 .get 成语最适合映射到 OP 代码中的“else”子句,这是我使用它的理由。
        • @cᴏʟᴅsᴘᴇᴇᴅ:在这种情况下,也许值得使用defaultdict。因为在这种情况下,它被明确选择为“默认”值。
        猜你喜欢
        • 2013-02-07
        • 1970-01-01
        • 2018-01-04
        • 2022-01-08
        • 2019-12-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多