【问题标题】:Remove common elements of hostnames (shorten hostnames) - DRY删除主机名的常见元素(缩短主机名) - DRY
【发布时间】:2013-11-27 10:42:48
【问题描述】:

说明:

我编写了以下函数来缩短生产中使用的机器的主机名。此处的名称已更改,但保留了相同的结构和格式。下面的代码很笨拙,我想将代码设为DRY (Don't Repeat Yourself)。可读性也很重要,因为这些代码可能不仅需要我自己维护或理解。

代码:

def shorten_hostnames(machines):
    # split items
    d = {k: v.split('.') for k, v in machines.items()}
    # trim end
    while all(d.values()):
        if not len(set([v[-1] for v in d.values()])) == 1:
            break
        if not all(len(v) > 1 for v in d.values()):
            break
        d = {k: v[:-1] for k, v in d.items()}
    # trim start
    while all(d.values()):
        if not len(set([v[0] for v in d.values()])) == 1:
            break
        if not all(len(v) > 1 for v in d.values()):
            break            
        d = {k: v[1:] for k, v in d.items()}
    # join items
    d = {k: '.'.join(v) for k, v in d.items()}
    # return shortened hostnames
    return d

示例输入:

machines = {'a.ace.site.info': 'a.ace.site.info',
            'b.ace.site.info': 'b.ace.site.info',
            'a.bob.site.info': 'a.bob.site.info',
            'b.bob.site.info': 'b.bob.site.info',} 

输出:

>>> for k, v in shorten_hostnames(machines).items():
    print k, '-->', v

b.ace.site.info --> b.ace
a.ace.site.info --> a.ace
b.bob.site.info --> b.bob
a.bob.site.info --> a.bob

我在哪里以及为什么需要您的帮助:

我试图嵌入一个函数,该函数将根据提供的参数从任一端进行修剪,但我不知道如何修改切片符号以从列表的开头或结尾进行修剪。我确信有一个我忽略的简单解决方案,无论是切片符号还是其他东西。

陷阱:

这里有几件事情需要提到,也就是你所说的“陷阱”。如果只有一个主机名被传递到函数中(例如machines = {'a.ace.site.info': 'a.ace.site.info'}),它应该只返回第一部分(在示例a 中)。另外 - 最终答案中不应有重复的结果。另外 - 主机名可以有不同的长度(段数不同)

事后思考:

一旦找到合适的解决方案,我将编辑问题标题和标签,以更好地反映如何将其应用于网站的未来访问者。例如,如果切片符号是解决方案(并且可以动态应用),我可能会修改问题以反映动态切片符号是问题的主题。

更多样本输入和预期输出:

# In
machines = {'ace.a.site.info': 'ace.a.site.info',
            'ace.b.site.info': 'ace.b.site.info',}
# Out
ace.b.site.info --> b
ace.a.site.info --> a

# In
machines = {'a.ace.site.info': 'a.ace.site.info',}
# Out
a.ace.site.info --> a

# In
machines = {'ace.a.site.info': 'ace.a.site.info',
            'ace.b.site.com': 'ace.b.site.com',}
# Out
ace.b.site.com --> b.site.com
ace.a.site.info --> a.site.info

【问题讨论】:

  • 您的意思是要从字符串列表中删除常见的前缀和后缀?
  • 顺便说一句,您的示例代码似乎产生了AttributeError: 'NoneType' object has no attribute 'items'
  • @MartijnPieters 是的。这就是我从示例中所理解的......
  • @MartijnPieters 试试这个,这应该会重现它pastebin.com/Lr9QZFxn
  • 所有主机名的长度是否相等;例如它们都有相同数量的点吗?

标签: python python-2.7 dry


【解决方案1】:

至少,将值和键拆分为单独的列表,然后在重构字典之前仅处理值,并使用短循环来选择开始和结束修剪的索引:

def shorten_hostnames(machines):
    keys, values = zip(*machines.items())
    values = [v.split('.') for v in values]
    for i, s in ((-1, slice(-1)), (0, slice(1, None))):
        while all(values):
            if not len(set(v[i] for v in values)) == 1:
                break
            if any(len(v) <= 1 for v in values):
                break
            values = [v[s] for v in values]
    return {k: '.'.join(v) for k, v in zip(keys, values)}

我会使用实用函数从序列序列中删除公共前缀,然后传入反向序列以删除尾随部分:

from itertools import dropwhile, izip_longest

def remove_common_prefix(*parts):
    # always leaves a last common element in place
    zipped = izip_longest(*(p[:-1] for p in parts), fillvalue=None)
    stripped = dropwhile(lambda v: len(set(v)) == 1, zipped)
    res = [filter(None, part) + (old[-1],) for part, old in zip(zip(*stripped), parts)]
    # filtered everything away? Then return just the last parts
    return res or [p[-1:] for p in parts]

def shorten_hostnames(machines):
    # edge-case; faster to just return the first part
    if len(machines) == 1:
        return {k: v.split('.', 1)[0] for k, v in machines.items()}
    keys, values = zip(*machines.items())  # for easier processing and re-assembling
    parts = remove_common_prefix(*(v.split('.')[::-1] for v in values))
    parts = remove_common_prefix(*(part[::-1] for part in parts))
    return {k: '.'.join(v) for k, v in zip(keys, parts)}

这会处理您的输入和长度不均匀的名称:

>>> shorten_hostnames(machines)
{'b.ace.site.info': 'b.ace', 'a.ace.site.info': 'a.ace', 'b.bob.site.info': 'b.bob', 'a.bob.site.info': 'a.bob'}
>>> shorten_hostnames({'foo': 'a.ace.site', 'bar': 'a.ace.site.info'})
{'foo': 'site', 'bar': 'site.info'}
>>> shorten_hostnames({'ace.a.site.info': 'ace.a.site.info', 'ace.b.site.info': 'ace.b.site.info'})
{'ace.b.site.info': 'b', 'ace.a.site.info': 'a'}
>>> shorten_hostnames({'ace.a.site.info': 'ace.a.site.info'})
{'ace.a.site.info': 'ace'}

【讨论】:

    【解决方案2】:
    def shorten_hostnames(machines):
        def trim(hostnames, head=True):
            while all(len(v) > 1 for v in hostnames) and len(set(v[0 if head else -1] for v in hostnames)) == 1:
                hostnames[:] = [v[1:] if head else v[:-1] for v in hostnames]
    
        keys, values = zip(*machines.items())
        hostnames = [v.split('.') for v in values]
        trim(hostnames, False)
        trim(hostnames)
        return {k: '.'.join(v) for k, v in zip(keys, hostnames)}
    

    【讨论】:

    • @InbarRose 您现在可以检查我的解决方案吗?
    猜你喜欢
    • 2017-02-16
    • 1970-01-01
    • 2012-09-22
    • 1970-01-01
    • 2013-12-26
    • 2012-04-22
    • 1970-01-01
    • 1970-01-01
    • 2012-12-24
    相关资源
    最近更新 更多