我知道可能有更好,更简单的方法。
为了使自己熟悉Python,这只是一次学习练习。
需要一个参数(位置参数):
4
(IPv4)或6
(IPv6)。 用法:
./randip.py 4
61.104.170.242
./randip.py 6
4bfc:391d:3ec8:68ef:0ec8:529b:166d:2ece
代码:
#!/usr/bin/env python3
from sys import argv
from random import randint, choice
from string import hexdigits
def random_ip(v):
if v == 4:
octets = []
for x in range(4):
octets.append(str(randint(0,255)))
return '.'.join(octets)
elif v == 6:
octets = []
for x in range(8):
octet = []
for x in range(4):
octet.append(str(choice(hexdigits.lower())))
octets.append(''.join(octet))
return ':'.join(octets)
else:
return
def main():
print(random_ip(int(argv[1])))
if __name__ == '__main__':
main()
#1 楼
Python通常被描述为一种“包含电池”的语言,这也不例外。有一个仅用于IP地址操作的模块,另一个模块用于生成随机数。放在一起,它们以一种更具可读性(IMO)的方式完全满足您的要求。
在此示例中,我假设变量
v
包含4
或6
。from random import getrandbits
from ipaddress import IPv4Address, IPv6Address
if v == 4:
bits = getrandbits(32) # generates an integer with 32 random bits
addr = IPv4Address(bits) # instances an IPv4Address object from those bits
addr_str = str(addr) # get the IPv4Address object's string representation
elif v == 6:
bits = getrandbits(128) # generates an integer with 128 random bits
addr = IPv6Address(bits) # instances an IPv6Address object from those bits
# .compressed contains the short version of the IPv6 address
# str(addr) always returns the short address
# .exploded is the opposite of this, always returning the full address with all-zero groups and so on
addr_str = addr.compressed
print(addr_str)
这里,
addr_str
将拥有完全随机的IPv4或IPv6地址。您甚至可以从这样的子网中生成随机地址:
from random import getrandbits
from ipaddress import IPv4Network, IPv4Address
# network containing all addresses from 10.0.0.0 to 10.0.0.255
subnet = IPv4Network("10.0.0.0/24")
# subnet.max_prefixlen contains 32 for IPv4 subnets and 128 for IPv6 subnets
# subnet.prefixlen is 24 in this case, so we'll generate only 8 random bits
bits = getrandbits(subnet.max_prefixlen - subnet.prefixlen)
# here, we combine the subnet and the random bits
# to get an IP address from the previously specified subnet
addr = IPv4Address(subnet.network_address + bits)
addr_str = str(addr)
print(addr_str)
此处,
addr_str
将始终包含IP地址,例如10.0.0.184
,10.0.0.42
等。它与IPv6地址的工作方式相同,除了在这种情况下,您必须导入IPv6Network
和IPv6Address
。#2 楼
以下是有关您的代码的一些想法。检查命令行参数
如果在不使用命令行参数的情况下调用代码,则该代码将失败,因为它尝试使用
argv[1]
也没有我建议,如果用户输入无效或无参数,则打印“使用情况”消息会很好。使用列表推导
列表推导是非常有用,非常Pythonic。精通他们真的很好。以下是使用它生成随机IPv4地址的方法:
'.'.join([str(randint(0,255)) for x in range(4)])
IPv6地址有点棘手,因为我们需要十六进制数字。
':'.join([hex(randint(2**16,2**17))[-4:] for x in range(8)])
之所以可行,是因为
randint
生成一个介于0x10000到0x20000之间的数字,然后我们选择最后四个十六进制数字。评论
\ $ \ begingroup \ $
谢谢。最后一部分有点聪明。我有一个类似的想法,但无法实现。列表理解也很不错,表达起来有些棘手。
\ $ \ endgroup \ $
–声音
18年7月26日在13:37
\ $ \ begingroup \ $
您可以使用0x10000和0x20000代替2 ** 16和2 ** 17,以提高可读性。
\ $ \ endgroup \ $
–301_Moved_Permanently
18年7月26日在13:40
\ $ \ begingroup \ $
值得一提的是,您不需要列表理解就可以成为列表。它可以代替发电机。
\ $ \ endgroup \ $
– David Z
18年7月26日在17:48
\ $ \ begingroup \ $
对于IPv6,可以省略组中的前导零,因此您不需要该技巧,只需对range中的x做':'。join(hex(randrange(0x10000))[2:] 8))
\ $ \ endgroup \ $
–茉莉花
18年7月27日在12:38
\ $ \ begingroup \ $
@Robin:的确如此,但我想使其看起来一致。就个人而言,我更喜欢PeterW。的答案。十分优雅!
\ $ \ endgroup \ $
–爱德华
18年7月27日在12:40
#3 楼
对于不熟悉Python的人,您已经养成了很好的习惯。并不是每个人都使用函数或先尝试使用if __name__ == '__main__'
后卫。话虽如此,我认为提供2个函数而不是单个函数会更有意义:
random_ipv4
和random_ipv6
。您还可以将生成器表达式输入
join
。它们的处理速度都比[]
+ for
+ append
更快,并且更易于阅读: :def random_ipv4():
return '.'.join(str(randint(0,255)) for _ in range(4))
def random_ipv6():
return ':'.join(
''.join(choice(hexdigits).lower() for _ in range(4))
for _ in range(8)
)
评论
\ $ \ begingroup \ $
CMIIW,但是当内存不是问题时,列表理解不是比生成器理解要快一点吗?
\ $ \ endgroup \ $
–处置不良
18年7月26日在14:01
\ $ \ begingroup \ $
@Ludisposed在bytes.com/topic/python/answers/…上发现了旧的计时,但是对于联接来说,它并不重要,因为当前的实现立即将输入转换为列表。
\ $ \ endgroup \ $
–301_Moved_Permanently
18年7月26日在14:49
\ $ \ begingroup \ $
@Ludisposed我自己的测试表明,使用生成器表达式对100万次random_ipv4调用的响应时间为5.792979179001122秒,使用列表理解对5.526552320003248的响应为5.526552320003248。没有哪个比另一个更确切地说服了。由于我们不打算重复使用结果,因此生成器表达式可以很好地说明这一点。
\ $ \ endgroup \ $
–301_Moved_Permanently
18年7月26日在15:01
\ $ \ begingroup \ $
是的,没关系,它取决于数据。我同意您选择发电机的理由。
\ $ \ endgroup \ $
–处置不良
18年7月26日在15:11
#4 楼
检查无效的输出,然后重新生成。您可能会产生保留用于特定目的的输出,例如回送(
127.0.0.1
或::1
)或广播(255.255.255.255
,ff02::1
,ff02::2
)。积累有关此类地址的知识,如果发现您已经生成了一个,则将其替换。您可以递归执行此操作:def make_address(v):
address = random_ip(v)
if is_reserved(address):
return make_address(v)
return address
评论
\ $ \ begingroup \ $
无需递归。 is_reserved(address):循环可能还需要一段时间。但是再说一次,如果生成有效IP的机会仅为千分之一(因此您有可能会定期达到堆栈大小限制),那么您可能做错了...
\ $ \ endgroup \ $
–地狱
18年7月26日在15:20
\ $ \ begingroup \ $
是的,我只是展示一种方式。 Python对我而言只是偶尔使用的一种语言,我不记得它是否可以保证消除尾部调用...
\ $ \ endgroup \ $
– Toby Speight
18年7月26日在16:13
\ $ \ begingroup \ $
然后,您可能需要其他选项来创建由映射IPv4地址和硬件MAC地址形成的IPv6地址。
\ $ \ endgroup \ $
– Toby Speight
18年7月26日在16:59
\ $ \ begingroup \ $
@TobySpeight没有,但是有一种方法可以通过使用装饰器来手动保证:stackoverflow.com/q/27417874/2415524
\ $ \ endgroup \ $
–mbomb007
18年7月27日在21:52
#5 楼
验证输入:目前,您的程序如果不带参数调用则中止
IndexError
,如果使用非整数参数调用则中止
ValueError
,打印
None
(如果使用不是4
或
6
的整数参数调用)。缺少或无效的参数应显示一条有用的错误消息。
大多数Unix命令行工具都将显示此消息。到标准错误
,并在失败的情况下以非零退出状态终止。
将给定参数与
字符串
"4"
和"6"
而不是将其转换为整数(可能会失败)。
使用列表理解而不是在循环中附加到数组。
将
_
用作如果不需要具体值,则使用迭代器变量。例如,可以将“ IPv4”情况实现为
if v == 4:
return '.'.join(str(randint(0,255)) for _ in range(4))
评论
\ $ \ begingroup \ $
为什么_?这是人们做的事情吗?我从没看过
\ $ \ endgroup \ $
–声音
18年7月26日在17:45
\ $ \ begingroup \ $
@ tjt263:按照惯例,它只是用作“丢弃变量”,例如,请参见stackoverflow.com/q/5893163/1187415
\ $ \ endgroup \ $
–马丁R
18年7月26日在18:27
#6 楼
由于您已经得到了一些答案,告诉您验证输入,因此这是使用argparse
模块的一种方法:import argparse
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('-v',
type=int,
choices=(4, 6),
default=4,
help="Whether to generate a random IPv4 or IPv6 address. Default: IPv4.")
args = parser.parse_args()
print(random_ip(args.v))
然后就可以使用它了在这样的命令行上:
./randip
./randip -v 4
./randip -v4
./randip -v 6
如果执行其他任何操作,则会打印一条有用的使用消息:
usage: [-h] [-v {4,6}]
optional arguments:
-h, --help show this help message and exit
-v {4,6} Whether to generate a random IPv4 or IPv6 address. Default:
IPv4.
评论
\ $ \ begingroup \ $
不错的解决方案-您是否考虑过在有条件之后将addr_str = addr.compressed推入?
\ $ \ endgroup \ $
–cmh
18年7月26日在15:07
\ $ \ begingroup \ $
@ tjt263是的,我想说这可能比您的方法更具教育意义,即使您真正想要的是生成随机IP地址的一种更好的方法。
\ $ \ endgroup \ $
– David Z
18年7月26日在17:46
\ $ \ begingroup \ $
在CodeReview中,用户并不是试图通过内置函数来尽可能高效地执行某些操作,而是希望正确地采用其方法。告诉某人从头开始编写IP生成器只是导入它并不能真正为社区服务。
\ $ \ endgroup \ $
–user1717828
18年7月26日在20:42
\ $ \ begingroup \ $
在发布它之前,我曾考虑在我的答案前加上诸如“这不是Code Review的正确答案,但是...”这样的内容,但我认为它不会像这样爆炸。我打算将其作为对将来的人们通过Google进行查找的有用资源,因为就在几个月前,我的处境与OP完全相同:想编写一个IP随机化器,却不知道Python可以为我做这件事。我在示例中添加了一些注释,希望它们使代码的工作方式更加明显。
\ $ \ endgroup \ $
– Peter W.
18年7月27日在6:58
\ $ \ begingroup \ $
@ user1717828不是。如果您愿意就CRSE提出反馈意见,那么您就天生就可以选择该问题的替代解决方案。如果您有目的地选择重新发明轮子(作为个人挑战或其他挑战),则有重新发明轮子的标签。
\ $ \ endgroup \ $
–丹尼尔(Daniel)
18年7月27日在23:12