[Python-ideas] ipaddress: Interface inheriting from Address
Jon Foster
jon at jon-foster.co.uk
Thu Aug 22 00:55:20 CEST 2013
Hi all,
I'd like to propose changing ipaddress.IPv[46]Interface to not inherit from IPv[46]Address.
(And I hope I've got the right mailing list?)
The "ipaddress" module is currently in the standard library on a provisional basis, so "backwards
incompatible changes may occur". But obviously there needs to be a very good reason. I think
there is.
The problem is that IPv[46]Interface can't currently be passed to a function that expects an
IPv[46]Address, because it redefines str(), ".exploded", ".compressed", "==" and "<" in an
incompatible way.
E.g suppose we have:
>>> from ipaddress import IPv4Address, IPv4Interface
>>> my_dict = {IPv4Address('1.2.3.4'): 'Hello'}
Obviously lookup with an IPv4Address works:
>>> addr = IPv4Address('1.2.3.4')
>>> print(my_dict.get(addr))
Hello
But with the IPv4Interface subclass, the lookup doesn't work:
>>> intf = IPv4Interface('1.2.3.4/24')
>>> print(my_dict.get(intf))
None
And that's because equality has been redefined:
>>> IPv4Address('1.2.3.4') == IPv4Interface('1.2.3.4/24')
False
When doing inheritance the usual expectation is that "Functions that use references to base classes
must be able to use objects of derived classes without knowing it". This is called the "Liskov
Substitution Principle". And IPv4Interface isn't following that principle.
More informally, since IPv4Interface inherits from IPv4Address you'd expect that an IPv4Interface
"is a" IPv4Address, but it really isn't. It's really a "has a" relationship, which is more
commonly done by giving IPv4Interface a property that's an IPv4Address.
This problem can't be solved easily by just redefining "==". If we let IPv4Address('1.2.3.4') ==
IPv4Interface('1.2.3.4/24'), and IPv4Address('1.2.3.4') == IPv4Interface('1.2.3.4/16'), then to
keep the normal transitive behaviour of equals we'd have to let IPv4Interface('1.2.3.4/16') ==
IPv4Interface('1.2.3.4/24'), and that seems wrong. Where people actually know they're comparing
IPv4Interface objects they will really want to compare both the IP address and netmask.
My proposed solution is just to change IPv4Interface to not inherit from IPv4Address.
IPv4Interface already has a "ip" property that gives you the IP address as a proper IPv4Address
object. So code written for Python 3.4, using the "ip" property, would be backward-compatible with
Python 3.3. And people could obviousy start writing code that way today, to be compatible with
both Python 3.3 and Python 3.4.
I.e. people should write code like this:
>>> extracted_address = intf.ip
>>> extracted_address
IPv4Address('1.2.3.4')
>>> print(my_dict.get(extracted_address))
Hello
I'm proposing that IPv4Interface would not have a (public) base class, and the only remaining
properties and methods on IPv4Interface would be:
__init__()
ip
network
compressed
exploded
with_prefixlen
with_netmask
with_hostmask
__eq__() / __ne__()
__hash__()
__lt__(), __gt__() etc
__str__()
And all these properties and methods would do exactly what they do today.
I.e. IPv4Interface becomes just a container for the "ip" and "network" fields, plus the parsing
code in __init__() and a few formatting and comparison functions. This is a lot simpler to
understand than the current design.
With this design, IPv4Interface wouldn't be comparable to IPv4Address or IPv4Network objects. If
the user wants to do a comparison like that, they can get the .ip or .network properties and
compare that. Explicit is better than implicit.
If there is interest in this idea, I'll try to put together a patch next week.
Thanks to anyone who's read this far. What do you think?
Kind regards,
Jon
More information about the Python-ideas
mailing list