在python中,有没有更好的方法来迭代两个列表以查找项目之间的关系?
我模拟ip列表和子网目录作为输入:在python中,有没有更好的方法来迭代两个列表以查找项目之间的关系?,python,performance,optimization,Python,Performance,Optimization,我模拟ip列表和子网目录作为输入: # ip address list ip_list = [ '192.168.1.151', '192.168.10.191', '192.168.6.127', '192.168.2.227', '192.168.2.5', '192.168.3.237', '192.168.6.188', '192.168.7.209', '192.168.9.10', # Edited: add some /28, /16 case '192.168.12.39',
# ip address list
ip_list = [
'192.168.1.151', '192.168.10.191', '192.168.6.127',
'192.168.2.227', '192.168.2.5', '192.168.3.237',
'192.168.6.188', '192.168.7.209', '192.168.9.10',
# Edited: add some /28, /16 case
'192.168.12.39', '192.168.12.58', '10.63.11.1', '10.63.102.69',
]
# subnet dict
netsets = {
'192.168.1.0/24': 'subnet-A', # {subnet: subnet's name}
'192.168.10.0/24': 'subnet-B',
'192.168.2.0/24': 'subnet-C',
'192.168.3.0/24': 'subnet-C',
'192.168.6.0/24': 'subnet-D',
'192.168.7.0/24': 'subnet-D',
'192.168.9.0/24': 'subnet-E',
# Edited: add some /28, /16 case
'192.168.12.32/28': 'subnet-F',
'192.168.12.48/28': 'subnet-G',
'10.63.0.0/16': 'subnet-I',
}
# ip address list ip_list =
[ '192.168.1.151', '192.168.10.191', '192.168.6.127', '192.168.2.227', '192.168.2.5', '192.168.3.237',
'192.168.6.188', '192.168.7.209', '192.168.9.10' ]
# subnet dict
netsets = { '192.168.1.0/24': 'subnet-A', # {subnet: subnet's name}
'192.168.10.0/24': 'subnet-B',
'192.168.2.0/24':'subnet-C',
'192.168.3.0/24': 'subnet-C',
'192.168.6.0/24': 'subnet-D',
'192.168.7.0/24': 'subnet-D',
'192.168.9.0/24':'subnet-E', }
然后,ip\u列表中的每个ip地址都需要找到子网的名称
我们假设每个ip地址都可以在netsets
中找到相应的子网
输出如下:
192.168.1.151 subnet-A
192.168.10.191 subnet-B
192.168.6.127 subnet-D
192.168.2.227 subnet-C
192.168.2.5 subnet-C
192.168.3.237 subnet-C
192.168.6.188 subnet-D
192.168.7.209 subnet-D
192.168.9.10 subnet-E
# add some /28, /16 case
192.168.12.39 subnet-F
192.168.12.58 subnet-G
10.63.11.1 subnet-I
10.63.102.69 subnet-I
192.168.1.151 subnet-A
192.168.10.191 subnet-B
192.168.6.127 subnet-D
192.168.2.227 subnet-C
192.168.2.5 subnet-C
192.168.3.237 subnet-C
192.168.6.188 subnet-D
192.168.7.209 subnet-D
192.168.9.10 subnet-E
我用来计算CIDR,下面是我的代码:
from netaddr import IPAddress, IPNetwork
def netaddr_test(ips, netsets):
for ip in ips:
for subnet, name in netsets.iteritems():
if IPAddress(ip) in IPNetwork(subnet):
print ip, '\t', name
break
netaddr_test(ip_list, netsets)
但是这段代码太慢,迭代太多。时间的复杂性为O(n**2)
一旦我们有成千上万的ip需要迭代,这段代码就花费了太多的时间
有没有更好的办法来解决这个问题
# ip address list
ip_list = [
'192.168.1.151', '192.168.10.191', '192.168.6.127',
'192.168.2.227', '192.168.2.5', '192.168.3.237',
'192.168.6.188', '192.168.7.209', '192.168.9.10'
]
# subnet dict
netsets = {
'192.168.1.0/24': 'subnet-A', # {subnet: subnet's name}
'192.168.10.0/24': 'subnet-B',
'192.168.2.0/24': 'subnet-C',
'192.168.3.0/24': 'subnet-C',
'192.168.6.0/24': 'subnet-D',
'192.168.7.0/24': 'subnet-D',
'192.168.9.0/24': 'subnet-E',
}
new_netsets = {}
for k,v in netsets.items():
new_netsets['.'.join(k.split('.')[:3])] = v
for IP in ip_list:
newIP = '.'.join(IP.split('.')[:3])
print IP, new_netsets[newIP]
希望这有帮助。我建议避免在for循环中创建新实例。这不会降低复杂性(会增加复杂性),但会加快NetAddress\u测试的速度,尤其是在多次调用时。例如:
def _init(ips, netsets):
"""Initialize all objects"""
new_ips = []
new_subs = {}
for ip in ips:
new_ips.append(IPAddress(ip))
for subnet, info in netsets.iteritems():
new_subs[subnet] = {'name': info, 'subnet': IPNetwork(subnet)}
return new_ips, new_subs
def netaddr_test(ips, netsets):
for ip in ips:
for stringnet, info in netsets.iteritems():
if ip in info['subnet']:
print ip, '\t', info['name']
break
ni, ns = _init(ip_list, netsets)
netaddr_test(ni, ns)
更新:使用
ip_list = [
'192.168.1.151', '192.168.10.191', '192.168.6.127',
'192.168.2.227', '192.168.2.5', '192.168.3.237',
'192.168.6.188', '192.168.7.209', '192.168.9.10'
] * 1000
结果:
# Original
$ time python /tmp/test.py > /dev/null
real 0m0.357s
user 0m0.345s
sys 0m0.012s
# Modified
$ time python /tmp/test2.py > /dev/null
real 0m0.126s
user 0m0.122s
sys 0m0.005s
现在,我从未使用过netaddr
,因此我不确定它在内部如何处理子网。在您的情况下,您可以将子网视为一系列IP,每个IP都是一个uint_32
,因此您可以将所有内容转换为整数:
# IPs now are
ip_list_int = [3232235927, 3232238271, ...]
netsets_expanded = {
'192.168.1.0/24': {'name': 'subnet-A', 'start': 3232235776, 'end': 3232236031}
netaddr
可用于将数据转换为上述格式。到达后,您的netaddr\u测试将变为(并且仅适用于整数比较):
def netaddr\u测试(ips、netset):
对于ip中的ip:
对于子网,netsets.iteritems()中的子信息:
如果ip>=subinfo['start']和ip
我模拟ip列表和子网目录作为输入:
# ip address list
ip_list = [
'192.168.1.151', '192.168.10.191', '192.168.6.127',
'192.168.2.227', '192.168.2.5', '192.168.3.237',
'192.168.6.188', '192.168.7.209', '192.168.9.10',
# Edited: add some /28, /16 case
'192.168.12.39', '192.168.12.58', '10.63.11.1', '10.63.102.69',
]
# subnet dict
netsets = {
'192.168.1.0/24': 'subnet-A', # {subnet: subnet's name}
'192.168.10.0/24': 'subnet-B',
'192.168.2.0/24': 'subnet-C',
'192.168.3.0/24': 'subnet-C',
'192.168.6.0/24': 'subnet-D',
'192.168.7.0/24': 'subnet-D',
'192.168.9.0/24': 'subnet-E',
# Edited: add some /28, /16 case
'192.168.12.32/28': 'subnet-F',
'192.168.12.48/28': 'subnet-G',
'10.63.0.0/16': 'subnet-I',
}
# ip address list ip_list =
[ '192.168.1.151', '192.168.10.191', '192.168.6.127', '192.168.2.227', '192.168.2.5', '192.168.3.237',
'192.168.6.188', '192.168.7.209', '192.168.9.10' ]
# subnet dict
netsets = { '192.168.1.0/24': 'subnet-A', # {subnet: subnet's name}
'192.168.10.0/24': 'subnet-B',
'192.168.2.0/24':'subnet-C',
'192.168.3.0/24': 'subnet-C',
'192.168.6.0/24': 'subnet-D',
'192.168.7.0/24': 'subnet-D',
'192.168.9.0/24':'subnet-E', }
然后ip_列表中的每个ip地址都需要找到
子网的名称
我们假设每个ip地址都可以在中找到相应的子网
netsets
输出如下:
192.168.1.151 subnet-A
192.168.10.191 subnet-B
192.168.6.127 subnet-D
192.168.2.227 subnet-C
192.168.2.5 subnet-C
192.168.3.237 subnet-C
192.168.6.188 subnet-D
192.168.7.209 subnet-D
192.168.9.10 subnet-E
# add some /28, /16 case
192.168.12.39 subnet-F
192.168.12.58 subnet-G
10.63.11.1 subnet-I
10.63.102.69 subnet-I
192.168.1.151 subnet-A
192.168.10.191 subnet-B
192.168.6.127 subnet-D
192.168.2.227 subnet-C
192.168.2.5 subnet-C
192.168.3.237 subnet-C
192.168.6.188 subnet-D
192.168.7.209 subnet-D
192.168.9.10 subnet-E
[……]
有没有更好的办法来解决这个问题
# ip address list
ip_list = [
'192.168.1.151', '192.168.10.191', '192.168.6.127',
'192.168.2.227', '192.168.2.5', '192.168.3.237',
'192.168.6.188', '192.168.7.209', '192.168.9.10'
]
# subnet dict
netsets = {
'192.168.1.0/24': 'subnet-A', # {subnet: subnet's name}
'192.168.10.0/24': 'subnet-B',
'192.168.2.0/24': 'subnet-C',
'192.168.3.0/24': 'subnet-C',
'192.168.6.0/24': 'subnet-D',
'192.168.7.0/24': 'subnet-D',
'192.168.9.0/24': 'subnet-E',
}
new_netsets = {}
for k,v in netsets.items():
new_netsets['.'.join(k.split('.')[:3])] = v
for IP in ip_list:
newIP = '.'.join(IP.split('.')[:3])
print IP, new_netsets[newIP]
这是一个两行程序:
for ip_addr in ip_list:
print "{0}\t{1}".format(ip_addr,netsets[".".join(ip_addr.split('.')[0:-1])+".0/24"])
我可以推荐使用特别优化的模块来快速搜索。因此,可以在O(m*logn)时间内解决该任务。例如:
from intervaltree import Interval, IntervalTree
from ipaddress import ip_network, ip_address
# build nets tree
netstree = IntervalTree(
Interval(
ip_network(net).network_address,
ip_network(net).broadcast_address,
name
)
for
net, name
in
netsets.items()
)
# Now you may check ip intervals
for i in ip_list:
ip = ip_address(i)
nets = netstree[ip]
if nets: # set is not empty
netdata = list(nets)[0]
print(netdata.data)
# prints 'subnet-E'
在一般情况下,若要测试N个模板和M个值是否匹配,那个么除了O(N*M)之外,并没有什么能做得更好。但是如果你能重新制定任务,你就可以加快它
我的建议是对模板进行分组,这样您就可以拥有几个高级模板,如果某个IP与之匹配,那么您就可以进入最终模板。在您的示例中,这将是
grouped_netsets = {
"192.168.0.0/16": {
'192.168.1.0/24': 'subnet-A', # {subnet: subnet's name}
'192.168.10.0/24': 'subnet-B',
'192.168.2.0/24': 'subnet-C',
'192.168.3.0/24': 'subnet-C',
'192.168.6.0/24': 'subnet-D',
'192.168.7.0/24': 'subnet-D',
'192.168.9.0/24': 'subnet-E',
}
}
def netaddr_test(ips, grouped_netsets):
for ip in ips:
for group, netsets in grouped_netsets.iteritems():
if IPAddress(ip) in IPNetwork(group):
for subnet, name in netsets.iteritems():
if IPAddress(ip) in IPNetwork(subnet):
print(ip, '\t', name)
break
所以,如果ip_列表包含任何不以192.168开头的内容,则只需一次检查即可将其删除
剩下的唯一问题是编写函数,用最佳配置对网络集进行分组。假设子网彼此不重叠,您可以将子网转换为两个整数,即范围的开始和结束。这些数字将被添加到一个将被排序的列表中。在执行此操作时,我们需要构建一个字典,该字典可用于以后检索以范围开头的子网名称
def to_int(ip):
parts = map(int, ip.split('.'))
return parts[0] << 24 | parts[1] << 16 | parts[2] << 8 | parts[3]
def build(netsets):
ranges = []
subnets = {}
for net, name in netsets.iteritems():
ip, size = net.split('/')
start = to_int(ip)
end = start | 0xffffffff >> int(size)
ranges.extend([start, end])
subnets[start] = name
ranges.sort()
return ranges, subnets
使用前面的构建块,您可以使用以下代码轻松获得每个IP的子网:
ranges, subnets = build(netsets)
for ip in ip_list:
print 'ip: {0}, subnet: {1}'.format(ip, find(ranges, subnets, ip))
构建字典和范围列表需要O(m log m)时间,而遍历IP列表需要O(n log m),其中m是子网的数量,n是IP的数量。解决方案适用于不同大小的不同子网,如果IP不属于任何子网,将打印None
。您是否只有/24个子网?如果是这样的话,你可以很容易地从IP地址中推断出子网。@alpha1554不,它可能是/16或其他,那么我认为你做得比O(n²)更好(我可能错了)。但是,您可以轻松地对该进程进行多线程处理。一些小的优化可能是预先计算IPNetwork(subnet)
部分。如果您不关心内存使用情况,还可以构建一个字典,其中包含子网的所有IP作为密钥。但这是一种内存非常昂贵的方法。如果您有一个要搜索的子网层次结构,那么构建一个子网层次结构可能会有所帮助。从最高阶子网(/0)节点开始,然后根据匹配情况沿左分支或右分支走下去。这平均应该是O(logn)。此外,看起来对解决您的问题很有用。当然,只有当nestets键以“.0/24”+结尾时,它才起作用。它适用于不同的网络掩码/24、/16,甚至一些奇怪的-/21;+复杂性O(n*logn)谢谢。他的答案是克里斯托夫·特拉萨的答案。使用IP集。