Learning Python now coming from Perl
Nick Craig-Wood
nick at craig-wood.com
Tue Dec 9 07:30:52 EST 2008
Roy Smith <roy at panix.com> wrote:
> In article <slrngjps0o.51b.nick at irishsea.home.craig-wood.com>,
> Nick Craig-Wood <nick at craig-wood.com> wrote:
>
> > My favourite mistake when I made the transition was calling methods
> > without parentheses. In perl it is common to call methods without
> > parentheses - in python this does absolutely nothing! pychecker does
> > warn about it though.
> >
> > perl -> $object->method
> > python -> object.method()
>
> On the other hand, leaving out the parens returns the function itself,
> which you can then call later. I've often used this to create data-driven
> logic.
I didn't say it wasn't useful, just that if you came from Perl like I
did, it is an easy mistake to make ;-)
> For example, I'm currently working on some code that marshals objects of
> various types to a wire protocol. I've got something like:
>
> encoders = {
> SM_INT: write_int,
> SM_SHORT: write_short,
> SM_FLOAT: write_float,
> # and so on
> }
>
> class AnyVal:
> def __init__(self, type, value):
> self.type = type
> self.value = value
>
> def write_anyval(any):
> encoders[any.type](any.value)
>
> The fact that functions are objects which can be assigned and stored in
> containers makes this easy to do.
OO lore says whenever you see a type field in an instance you've gone
wrong - types should be represented by what sort of object you've got,
not by a type field.
Eg http://www.soberit.hut.fi/mmantyla/BadCodeSmellsTaxonomy.htm
"""The situation where switch statements or type codes are needed
should be handled by creating subclasses. """
Here is my first iteration (untested)
class AnyVal:
def __init__(self, value):
self.value = value
def write(self):
raise NotImplementedError()
class IntVal(AnyVal):
def write(self):
# write_int code
class ShortVal(AnyVal):
def write(self):
# write_short code
class FloatVal(AnyVal):
def write(self):
# write_float code
Then to write an AnyVal you just call any.write()
The initialisation of the AnyVals then becomes
from AnyVal(int_expression, SM_INT)
to IntVal(int_expression)
However, if the types of the expressions aren't known until run time,
then use a dict of class types
AnyValRegistry = {
SM_INT: IntVal,
SM_SHORT: ShortVal,
SM_FLOAT: FloatVal,
# and so on
}
And initialise AnyVal objects thus
any = AnyValRegistry[type](value)
This smells of code duplication though and a real potential for a
mismatch between the AnyValRegistry and the actual classes.
I'd probably generalise this by putting the type code in the class and
use a __metaclass__ to autogenerate the AnyValRegistry dict which
would then become an attribute of AnyClass
Eg (slightly tested)
SM_INT=1
SM_SHORT=2
SM_FLOAT=3
class AnyVal(object):
TYPE = None
registry = {}
class __metaclass__(type):
def __init__(cls, name, bases, dict):
cls.registry[cls.TYPE] = cls
def __init__(self, value):
self.value = value
@classmethod
def new(cls, type_code, value):
"""Factory function to generate the correct subclass of AnyVal by type code"""
return cls.registry[type_code](value)
def write(self):
raise NotImplementedError()
class IntVal(AnyVal):
TYPE = SM_INT
def write(self):
# write_int code
print "int", self.value
class ShortVal(AnyVal):
TYPE = SM_SHORT
def write(self):
# write_short code
print "short", self.value
class FloatVal(AnyVal):
TYPE = SM_FLOAT
def write(self):
# write_float code
print "float", self.value
You then make new objects with any = AnyVal.new(type_code, value) and
write them with any.write()
Anyone can add a subclass of AnyVal and have it added to the
AnyVal.registry which is neat.
>>> any = AnyVal.new(SM_SHORT, 1)
>>> any
<__main__.ShortVal object at 0xb7e3776c>
>>> any.write()
short 1
>>> any = AnyVal.new(SM_FLOAT, 1.8)
>>> any
<__main__.FloatVal object at 0xb7e37a6c>
>>> any.write()
float 1.8
You could also override __new__ so you could write AnyVal(type_code,
value) to create the object of a new type. I personally don't think
its is worth it - a factory function is nice and obvious and show
exactly what is going on.
--
Nick Craig-Wood <nick at craig-wood.com> -- http://www.craig-wood.com/nick
More information about the Python-list
mailing list