IP 地址 - Python:ipaddress 属性错误:"str"对象没有属性



根据这里给出的建议,我使用检查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')

最新更新