[Python-3000] Use case for generics
Talin
talin at acm.org
Sat May 13 23:08:22 CEST 2006
One way of thinking about generic functions is that they are the dynamic
languages' equivalent to C++ function overloading. Now, there are lots
of valid (and some not-so-valid) use cases for function overloading; I'm
just going to pick one of the most common ones.
Many GUI libraries have the concept of a "Rectangle" class which
represents the area of a widget. Typically, a rectangle can be
constructed in several ways:
1) From 4 scalars:
r = Rectangle( x, y, w, h )
2) From two points, representing the top left and lower right corners:
r = Rectangle( minpos, maxpos )
3) From a position and a size:
r = Rectangle( position, size )
In order to support this last use case, a special "Size" class is
defined which is distinct from the "Point" class, and which has "width,
height" members instead of "x, y" members. (Essentiallly points and
sizes are both 2D vectors, where points represent absolute locations and
sizes represent relative locations.)
The following is an implementation, in Python, of a hypothetical
Rectangle class using generic functions, followed by the same class
without generics. I've tried my best to make the non-generic version as
non-obfuscated as possible, so that the comparison would be fair.
(The non-generic version doesn't support keyword args, because I
couldn't figure out how to do it cleanly; but I'm not sure whether the
generic version would support keywords or not.)
(And I apologize that my whitespace conventions don't match the
standard; I work hard to fix that in the PEPs, but its too much effort
to fix that for this example.)
# Indicates a location in 2D space
class Point( object ):
__slots__ = ( 'x', 'y' )
def __init__( self, x, y ):
self.x = x
self.y = y
# The size of an item in 2D space
class Size( object ):
__slots__ = ( 'w', 'h' )
def __init__( self, w, h ):
self.w = w
self.h = h
# A rectanglar area in 2D space
class Rectangle( object ):
__slots__ = ( 'x', 'y', 'w', 'h' )
def __init__( self, x=0, y=0, w=0, h=0 ):
self.x = x
self.y = y
self.w = w
self.h = h
def __init__( self, position:Point, size:Size ):
self.x, self.y = position.x, position.y
self.w, self.h = size.w, size.h
def __init__( self, ulpos:Point, lrpos:Point ):
self.x, self.y = ulpos.x, lrpos.y
self.w = lrpos.x - ulpos.x
self.h = lrpos.y - ulpos.y
# Same implementation but without generics
class Rectangle( object ):
__slots__ = ( 'x', 'y', 'w', 'h' )
def __init__( self, *args ):
if len( args ) == 2 and isinstance( args[ 0 ], Point ):
if isinstance( args[ 1 ], Point ):
self.from_ul_lr( *args )
elif isinstance( args[ 1 ], Size ):
self.from_pos_size( *args )
else:
raise TypeError
else:
self.from_scalars( *args )
def from_scalars( self, x=0, y=0, w=0, h=0 ):
self.x = x
self.y = y
self.w = w
self.h = h
def from_pos_size( self, position:Point, size:Size ):
self.x, self.y = position.x, position.y
self.w, self.h = size.w, size.h
def from_ul_lr( self, ulpos:Point, lrpos:Point ):
self.x, self.y = ulpos.x, lrpos.y
self.w = lrpos.x - ulpos.x
self.h = lrpos.y - ulpos.y
-- Talin
More information about the Python-3000
mailing list