解释
我编写了以下函数来缩短生产中使用的机器的主机名。这里的名称已更改,但保留了相同的结构和格式。下面的代码很笨重,我想编写代码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
我需要你的帮助的地点和原因:
我试图嵌入一个函数,可以根据提供的参数从任一端进行修剪,但我无法弄清楚如何修改切片表示法以从列表的开头或结尾进行修剪。我确信我可以忽略一个简单的解决方案,无论是切片符号还是别的东西。
疑难杂症的:
这里需要提到的一些事情就是你所谓的“Gotcha”。如果只有一个主机名传递给函数(例如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
答案 0 :(得分:4)
至少,将值和键拆分为单独的列表,然后在重构字典之前处理这些值,并使用短循环选择开始和结束修剪的索引:
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'}
答案 1 :(得分: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)}