Roy Smith <[EMAIL PROTECTED]> wrote: > In article <[EMAIL PROTECTED]>, > Nick Craig-Wood <[EMAIL PROTECTED]> 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 <[EMAIL PROTECTED]> -- http://www.craig-wood.com/nick -- http://mail.python.org/mailman/listinfo/python-list