根据这里给出的建议,我使用检查ipaddress
模块执行类型为的检查
In [25]: IPv4Address(u'100.64.1.1') in IPv4Network(u'100.64.0.0/10')
Out[25]: True
在IPython中运行良好。然而,当我把它变成一个函数时:
import ipaddress
def isPrivateIp(ip):
ip4addressBlocks = [u'0.0.0.0/8', u'10.0.0.0/8', u'100.64.0.0/10', u'127.0.0.0/8', u'169.254.0.0/16', u'172.16.0.0/12', u'192.0.0.0/24', u'192.0.2.0/24', u'192.88.99.0/24',
u'192.168.0.0/16', u'198.18.0.0/15', u'198.51.100.0/24', u'203.0.113.0/24', u'224.0.0.0/4', u'240.0.0.0/4', u'255.255.255.255/32']
unicoded = unicode(ip)
if any(unicoded in ipaddress.IPv4Network(address) for address in ip4addressBlocks):
return True
else:
return False
print isPrivateIp(r'169.254.255.1')
我得到:
File "isPrivateIP.py", line 14, in <module>
print isPrivateIp(r'169.254.255.1')
File "isPrivateIP.py", line 9, in isPrivateIp
if any(unicoded in ipaddress.IPv4Network(address) for address in ip4addressBlocks):
File "isPrivateIP.py", line 9, in <genexpr>
if any(unicoded in ipaddress.IPv4Network(address) for address in ip4addressBlocks):
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/ipaddress.py", line 705, in __contains__
if self._version != other._version:
AttributeError: 'str' object has no attribute '_version'
为什么会这样?
检查unicode字符串ip
是否在网络中,而在使用IPv4Address
实例之前。
你的测试必须是
if any(IPv4Address(unicoded) in ipaddress.IPv4Network(address) for address
in ip4addressBlocks):
@Phillip的答案是正确的,我只想进一步解释一下。我认为,从你的其他问题来看,你一下子就突破了许多编程和Python特定的想法,一些解释可能会有所帮助,或者至少证明它是密集的代码,但不是魔术,如果你想的话,可以给你谷歌一些其他术语。
对象
这是一个大话题,没有简单的教程介绍,所以我不打算尝试链接一个。但是,当您执行IPv4Address(u'100.64.1.1')
时,它并不是,只是测试IP地址是否为有效地址是或否,它需要大量处理IP地址的代码,将其封装在您提供的地址周围,并返回整个过程。
就像托尼·斯塔克穿上钢铁侠套装一样,这需要基本的文本,并为您提供一个合适的IPv4Address对象,其中包含大量针对IP地址的增强功能。
IPv4Network()
与之类似,它采用您的网络地址文本,并将其封装在一系列用于IP网络的增强功能中。
遏制
当您执行IPv4Address(u'100.64.1.1') in IPv4Network(u'100.64.0.0/10')
时,它是额外的增强代码,可以理解一个网络"在"另一个网络中意味着什么。
在您的问题中,您的代码正在执行:此时unicoded in ipaddress.IPv4Network(address)
和unicoded
仍然只是文本。
"word" in "sentence with some words"
是一种测试一个文本字符串是否在另一个字符串中的方法,因此您的代码正在进行一半的文本成员资格测试和另一半的IPv4Network成员资格测试。这种不匹配导致了失败和你看到的错误。IPv4Network对象无法处理。
(关键字in
在后台调用Python特殊方法__contains__
来处理这种测试)。
理解
在你的另一个问题中,询问Python中更好的做事方式(这是一个很好的问题),代码来自于经典的for
循环形状(例如假代码):
for network in networks:
test if address in network:
到这一个线形:
[if address in network for network in networks]
这种重塑的单行版本是一种列表理解,它是一种特殊情况,适用于"为列表中的每一项做点什么,并将结果汇总到另一个列表中"的常见模式。例如
>>> nums = [2, 4, 6, 8, 10, 12]
>>> results = [num+1 for num in nums]
>>> results
[3, 5, 7, 9, 11, 13]
>>> results = = [num > 5 for num in nums]
[False, False, True, True, True, True]
(更具体地说,如果没有外部的[
]
,这是一样的东西,但现在被称为生成器理解,这是一个更高内存效率的更新版本)。
任何
然后any()
函数将列表汇总为一个结果。它根据真值True/False来考虑列表,如果列表中的任何事物为True,则返回True。来自上方:
>>> results
[False, False, True, True, True, True]
>>> any(results)
True
(它与all()
函数一起测试列表中的所有内容是否为True)。
因此,将所有这些组合在一起得到的代码行是:
if any(ipaddress.IPv4Address(unicoded) in ipaddress.IPv4Network(address) for address in ip4addressBlocks):
print "it's reserved"
但(imho)这仍然很丑陋。一方面它写了很多"ipaddress",一方面它一遍又一遍地构建增强的对象,一方面变量名"ip4addressBlocks"没有解释它们的意义,另一方面你冗余地测试true/false,然后返回true/false。
所以,在这里,我:
- 从模块中只导入这两个名称,这样我就可以直接使用它们,而无需到处写
ipaddress.
- 在开始时生成一次保留列表IPv4Network对象
- 生成IPv4Address对象一次
- 运行测试并直接返回值,去掉"if true return true else return false"
- 我不知道你为什么把unicode和原始字符串放得到处都是,但这似乎没有必要,所以我把它删除了
例如:
from ipaddress import IPv4Address, IPv4Network
reserved_networks = [IPv4Network(x) for x in [
'0.0.0.0/8', '10.0.0.0/8', '100.64.0.0/10', '127.0.0.0/8',
'169.254.0.0/16', '172.16.0.0/12', '192.0.0.0/24', '192.0.2.0/24',
'192.88.99.0/24', '192.168.0.0/16', '198.18.0.0/15', '198.51.100.0/24',
'203.0.113.0/24', '224.0.0.0/4', '240.0.0.0/4', '255.255.255.255/32'
]]
def isPrivateIp(ip):
# True if ip is in a reserved range, otherwise False
ip = IPv4Address(ip)
return any((ip in net) for net in reserved_networks)
print isPrivateIp('179.254.255.1')