在Python列表中附加每个列表项的所有可能转换

时间:2015-02-01 00:12:08

标签: python list

我有一个可能的密码列表,我需要在此列表中附加每个密码的简单转换。说我的清单是

['sauce', 'banana']

我在这里显示了一系列转换。

'A' - > '@'

'S' - > '$'

然后,我希望将每个可能的转换添加到列表中。所以现在列表看起来应该像

['$auce', 's@uce', '$@uce', 'b@nana', 'ban@na',
 'banan@', 'b@n@na', 'b@nan@,' 'ban@n@', 'b@n@n@']

我将如何在Python中执行此操作?

我首先尝试创建一个完成所有转换的函数。然后我拿了那个变换后的字符串,基本上用原始字符串做了一个交叉产品。然而,这会导致很多重复,而且看起来有点笨拙。

功能:

def symbolize(s):
    options = {
        'a': '@',
        'S': '$'
    }
    copy = ''
    for i in range(len(s)):
        if s[i] in options:
            copy += options[s[i]]
        else:
            copy += s[i]
    return copy

然后是交叉产品:

for x in range(len(candidates)):
    candidates += list(''.join(t) for t in itertools.product(
        *zip(candidates[x], symbolize(candidates[x]))))

3 个答案:

答案 0 :(得分:3)

from itertools import product

def all_versions_of_word(word, alt_chars, skip_orig=True):
    chars = [ch + alt_chars.get(ch, "") for ch in word]
    combos = product(*chars)
    if skip_orig and word: next(combos)  # drop the first item
    return ("".join(c) for c in combos)

def transform_passwords(passwords, alt_chars={"a":"@", "s":"$"}):
    for word in passwords:
        yield from all_versions_of_word(word, alt_chars)

一样运行
>>> list(transform_passwords(['sauce', 'banana']))
['s@uce',
 '$auce',
 '$@uce',
 'banan@',
 'ban@na',
 'ban@n@',
 'b@nana',
 'b@nan@',
 'b@n@na',
 'b@n@n@']

答案 1 :(得分:2)

如果你想要你可以使用递归,虽然pytohn将它限制在2000的深度:

创建映射和列表:

lst = ['sauce', 'banana']
mapping = {'a':'@', 's':'$'}

现在递归地生成所有可能性(包括根本没有替换):

def opts(_mapping, _str):
    if not _str:
       yield ""
    else:
        for opt in opts(_mapping, _str[1:]):
            if _str[0] in _mapping:
                yield _mapping[_str[0]] + opt
            yield _str[0] + opt

输出:

[list(opts(mapping, st)) for st in lst]

=> [['$@uce', 's@uce', '$auce', 'sauce'], ['b@n@n@', 'ban@n@', 'b@nan@', 'banan@', 'b@n@na', 'ban@na', 'b@nana', 'banana']]

答案 2 :(得分:0)

我真的很开心地挖掘这个答案!这里有很多迭代字符串,但我喜欢我的答案!

import functools

def transform(pwd, subs):
    result = {pwd}
    stack = [pwd]
    # contains all resolved strings left to permute on
    while True:
        pwd = stack.pop()
        # grab a password

        if not stack and not any(i in subs for i in pwd):
            return result
            # if the stack is empty and is no way to permute further,
            #  then return our result.

        for idx,ch in enumerate(pwd):
            if ch in subs:
                repl = subs[ch]
                transformation = pwd[:idx]+repl+pwd[idx+1:]
                # transformation is our transformed word
                result.add(transformation)
                # add our transformation to our result set
                stack.append(transformation)
                # and toss it on our stack to transform further

def transform_multiple(pwds, subs):
    return functools.reduce(set.union,
                            (transform(pwd, subs) for pwd in pwds))

样本:

In [55]: transform_multiple(['banana', 'sauce','ananas'], {'a':'@', 's':'$'})
Out[55]:
{'$@uce',
 '$auce',
 '@n@n@$',
 '@n@n@s',
 '@n@na$',
 '@n@nas',
 '@nan@$',
 '@nan@s',
 '@nana$',
 '@nanas',
 'an@n@$',
 'an@n@s',
 'an@na$',
 'an@nas',
 'anan@$',
 'anan@s',
 'anana$',
 'ananas',
 'b@n@n@',
 'b@n@na',
 'b@nan@',
 'b@nana',
 'ban@n@',
 'ban@na',
 'banan@',
 'banana',
 's@uce',
 'sauce'}

如果我要花更多时间在这上面,我可能会删除if not stack and not any(...)调用并在for idx,ch in enumerate循环中放置一个标记,标记是否有变化对该字符串进行处理,然后在循环结束后测试if not flag and not stack: return result。每次循环时,这将为我们节省pwdlen(pwd)成员资格测试的整个迭代次数。