[Python-Dev] OT: Performance vs. Clarity vs. Convention

Patrick K. O'Brien pobrien@orbtech.com
Wed, 5 Jun 2002 21:15:51 -0500


[Guido van Rossum]
>
> I am more worried about the inefficiency of instantiating
> self.__class__ and then throwing it away after calling str() on it.
> You could factor out the body of __str__ into a separate method so
> that you can invoke it from __call__ without creating an instance.

Some more code from the module might help explain this design decision. I'm
still sort of toying with this to see if I like it. The basic idea here is
that I'm trying to support both DOM-like xhtml structures as well as simple
function-like callables that return strings. When the instance is called it
needs a fresh state in order to better mimic a true function. It isn't
immediately obvious to me how I might refactor this to avoid instantiating a
throwaway.

class Element:

    def __init__(self, klass, id, style, title):
        self.name = self.__class__.__name__.lower()
        self.attrs = {
            'class': klass,  # Space-separated list of classes.
            'id': id,        # Document-wide unique id.
            'style': style,  # Associated style info.
            'title': title,  # Advisory title/amplification.
            }

    def attrstring(self):
        attrs = self.attrs.keys()
        attrs.sort()  # Sorting is only cosmetic, not required.
        l = []  # List of formatted attribute/value pairs.
        for attr in attrs:
            value = self.attrs[attr]
            if value is not None and value != '':
                l += ['%s="%s"' % (attr, convert(value))]
        s = ' ' + ' '.join(l)  # Prepend a single space.
        return s.rstrip()  # Reduce to an empty string if no attrs.

    def __str__(self):
        pass

    def __repr__(self):
        return repr(self.__str__())


class EmptyElement(Element):

    def __init__(self, klass=None, id=None, style=None, title=None):
        Element.__init__(self, klass, id, style, title)

    def __call__(self, klass=None, id=None, style=None, title=None):
        o = self.__class__(klass, id, style, title)
        return str(o)

    def __str__(self):
        attrstring = self.attrstring()
        return '<%s%s />\n' % (self.name, attrstring)


class SimpleElement(Element):

    def __init__(self, content='', klass=None, id=None, style=None,
title=None):
        self.content = content
        Element.__init__(self, klass, id, style, title)

    def __call__(self, content='', klass=None, id=None, style=None,
title=None):
        o = self.__class__(content, klass, id, style, title)
        return str(o)

    def __str__(self):
        attrstring = self.attrstring()
        return '<%s%s>\n%s\n</%s>\n' % \
               (self.name, attrstring, convert(self.content), self.name)


class Br(EmptyElement): pass
class Hr(EmptyElement): pass
class P(SimpleElement): pass

# The following singleton instances are callable, returning strings.
# They can be used like simple functions to return properly tagged contents.
br = Br()
comment = Comment()
hr = Hr()
p = P()


> BTW I have another gripe about your example.
>
> >     def __str__(self):
> >         return '<!-- %s -->' % self.content
> >
> >     def __repr__(self):
> >         return repr(self.__str__())
>
> This definition of __repr__ makes no sense to me -- all it does is add
> string quotes around the contents of the string (and escape
> non-printing characters and quotes if there are any).  That is
> confusing, because it will appear to the reader as if the object is a
> string.

Yes. This was a conscious design choice for this particular application.
Maybe there is a better way, and maybe I'm not being too Pythonic, but I'm
not particularly troubled by this even though I know I'm "breaking the
rules".

I guess I don't mind if there is more than one way to do something, as long
as one way is the Python way and the other way is my way. ;-)

---
Patrick K. O'Brien
Orbtech