net = ipaddress.ip_network('1::/32') print('Number of addresses is like expected size:', net.num_addresses == 2**(128-32))Number of usable hosts should be 2**(128-32)
The class has no method for len. Let's do one:
ipaddress.IPv6Network.__len__ = lambda self: self.num_addresses net = ipaddress.ip_network('1::/32') print(net.__len__()) len(net) # BOOOM
Error:---------------------------------------------------------------------------
OverflowError Traceback (most recent call last)
<ipython-input-52-fefbadd730e7> in <module>
----> 1 len(net)
OverflowError: cannot fit 'int' into an index-sized integer
class Foo: def __len__(self): return 79228162514264337593543950336 len(Foo())
Error:---------------------------------------------------------------------------
OverflowError Traceback (most recent call last)
<ipython-input-64-955ff12672b5> in <module>
----> 1 len(Foo())
OverflowError: cannot fit 'int' into an index-sized integer
Now let's find out which number is too big:class Foo: def __init__(self, len): self.len = len def __len__(self): return self.len for n in itertools.count(2**63): try: len(Foo(n)) except OverflowError: print('Last possible value for len is:', n-1) breakAfter some tests, I know that following does not fail:
len(Foo(2**62))
But this will fail:
len(Foo(2**63))
Doing the test above have to do 4611686018427387904 to reach from 2**62 to 2**63.
So this a little bit big for iteration, so we better calculate it and try
to make assumptions.
So let's try 2**63-1:
len(Foo(2**63-1))
Output:9223372036854775807
From where does this number come from?import sys len(Foo(2**63-1)) == sys.maxsize
Output:True
Now you can understand why they have not implemented __len__.It's senseless to implement something, where you know from the beginning on,
that IPv6-Adresses have 128 Bit, which is a much bigger number as 2**63-1.
By the way, the iteration is still running. This approach was thought too simple :-D
I did not know this fact, that len() has an upper limit and I guess it comes from the implementation and can depend on architecture (32/64 bit).
Guessing: Maybe it comes from the limitation of addressing memory. The limit on 64 bit systems are usually 64 bit.
Some 32 bit CPUs can address more than 32 bit with PAE if they have more than 32 physical lines connected to memory for addressing.
Maybe you can look from where this limitation comes exactly and why.
The slice notation doesn't have this problem, so we are still able to address more than 2**63 in a file or iterable.
But if the object implements the __len__ method, it will fail. It will also fail, if you use this for iteration.
So if you would implement an iterator for IPv6Address, you could just make an
__iter__
method and use a generator:# definition def my_iter(self): yield from self.hosts() # monkey patching on ipaddress.IPv6Network.__iter__ = my_iter # monkey patching off del my_iter # we don't need it net = ipaddress.ip_network('1::/32') # using directly need the iter function # it's not an Iterator, because there is no next method net_iter = iter(net) # then manually do next next(net_iter) next(net_iter) next(net_iter)Let's make and IPv6Address Iterator just for fun:
def my_iter(self): return self def my_next(self): if not hasattr(self, 'hosts_generator'): self.hosts_generator = self.hosts() return next(self.hosts_generator) # if StopIteration was rised by generator # it bubbles up to the caller # if the caller is a for-loop, the loop stops # monkey patching on ipaddress.IPv6Network.__iter__ = my_iter ipaddress.IPv6Network.__next__ = my_next # monkey patching off del my_next, my_iter # we don't need it net = ipaddress.ip_network('1::/32') next(net) next(net)I think the original implementation is good as it is with paying attention about the corner cases.
Almost dead, but too lazy to die: https://sourceserver.info
All humans together. We don't need politicians!
All humans together. We don't need politicians!