[Tutor] [Re: class/type methods/functions]

A.T.Hofkamp a.t.hofkamp at tue.nl
Fri Oct 31 13:56:57 CET 2008


spir wrote:
>> After reading your mail, I cannot help wondering that something 
> crucial seems to be missing in your class structure. (I get the 
> impression that you are trying to squeeze object information and object 
> meta information together in one class definition.)
>>
> Yes. This is a consequence of type & instance beeing defined in the same 
> class() code section. If I unserstand well, what you call 
> meta_information is for me simply type attributes. They will be 
> available to any object of this type. Is this a misuse of types/classes? 

Not necessarily, it kind of depends on how you look at the problem. In 
programming there are usually many good solutions.

However, by moving the 'type' information to a seperate object, your classes 
become less cluttered, and the problem (and thus the solution) becomes easier 
to understand/program.


[I have moved this piece of discussion up, since I think it is the core of the 
structuring gap I experince.]
 >> Have you considered using the factory pattern? (ie make an object
 > that represents the type, and another one for the data) In that way you
 > can nicely seperate both kinds of information.
 >>
 > Definitely! That is the pattern I'm using -- or trying. I didn't know it
 > has a name.  Now, isn't this factory<->object pattern precisely what the
 > type<->instance relationship is supposed to be?

No.

The factory pattern is best explained by considering a real factory where you 
make products.
You have a Factory class + factory object (usually 1) that represents the real 
factory, and you have a Product class + many prduct objects for the products 
it makes.

Each product object has properties and functions (ie its color and how you can 
use it).
Meta information, like what kind of products exist and how to make/transform 
them however is *not* part of the product object but belongs in the factory 
object.
(Look at real life, you can use a car to get from A to B and back, but a car 
cannot tell you what kinds of other cars exist, or how to install eg a radio.)

In your application, that would mean that the things you call 'instance' above 
are part of eg the 'format' object, and things you name 'type' above, are part 
of the 'formatfactory' (or 'formattype') object.

The difference between your approach and the factory pattern imho is that the 
'type' information of an object is not stored in its class, but in another 
object (of class Factory).


> Unfortunately, I can't paste right now a full & real snippet of the, as 
> it is beeing refactored: it would rather be confusing. Here is a kind of 
> outline of a typical symbol type:

oke.
I used this example mainly to understand better what you are trying to do.
I wrote some comments about things I noticed and observations I made.


> class Format(Symbol):
>   ''' block formats symbol '''
>   # import relevant config data for this symbol type
>   from_codes = config.formats.from_codes
>   to_codes = config.formats.to_codes
>   names = config.formats.names
>   # open_format to be checked with closing tag (xhtml only)
>   open_format = None
>   def __init__(self, source, mark, section_level, posV, posH, close = 
> False,
>                   list_type = None, list_level=0):
>       # posV & posH ~ line & element numbers for error output
>       # 'mark' can be a wiki code or an xhfml tag
>       self.section_level = section_level        # = current title level
>       self.indent = section_level * TAB         # for nicer xhtml output

As a side-note:
self.indent can be computed from self.section_level. In general, it is better 
in such cases not to store the computable attribute, but instead to compute 
whenever you need it.
This strategy prevents that you get inconsistencies in your data (ie suppose 
that self.section_level == 1 and self.indent == TAB + TAB at some point in the 
program, which of both is then the true value?)

>       self.close = close                        # for automatic closing 
> (wiki only)
>       self.list_level = list_level              # nesting level
>       self.list_type = list_type                # bullet or number
>       self.error = False                        # flag used for no output

As a side-note:
you may want to consider using doc strings for documenting your class and 
instance variables, see eg pydoc or epydoc.


A variable like "section_level" leads me to believe that Format class is a 
part of a page.



>       # read & record symbol data
>       if source == WIKI:
>           self.from_wiki(mark, posV, posH)
>           return
>       if source == XHTML:
>           self.from_xhtml(mark, posV, posH)

This looks like a (factory!) class hierarchy you currently don't have. In OOP 
I would expect something like

self.load(...)    # 'self.from()' is not possible, since 'from' is a reserved 
word in Python

and the underlying object type of 'self' would decide the conversion you 
actually perform.

> So, as you can see, symbols of the Format type will use data held by 
> their type. This seems rather an obvious and consistent practice to me. 
> Namely, the from_codes, to_codes and names type attributes are available 
> to future Format instances. these attributes were imported from an 
> config module/object built at startup (from a config file).

In the factory pattern, you'd have a formattype object (of class FormatType) 
that holds the global (config?) information, and each Format object may hold a 
reference to formattype (if you want).


> Now, imagine it was a wiki text instead, and the user wishes to output 
> it into a wiki language different of the one it was originally coded in. 
> At some time between the reading & writing will the config been rebuilt 
> and, as a consequence, data held by Symbol types. As this is an action 
> of the type, I wish it to be performed by the type. So:

That's one option. Another option may be to make the page object 
language-agnostic (ie you have a 'list', a 'picture' and many more page 
elements, but not attached to a specific language.)

Also, you have a number of language objects that know how to convert the page 
to their language.
A few class definitions to make it a bit clearer (hopefully):


class Language(object):
     """
     Base class containing symbols/tags of any language used in your 
application. It also states what operations you can do, and has common code.
     """
     def __init__(self):
         # Setup common attributes (valid for all languages)

     def load_page(self, ....):
         """ Load a page """
         raise NotImplementedError("Implement me in a derived class")

Language class is not really needed, but very nice to make clear what 
operations you can do with it.



class WikiLanguage(Language):
     """
     Properties and symbols/tags that exist in the wiki language
     """
     def __init__(self):
         Language.__init__()
         self.list_tag = '*'

     def load_page(self, ....):
         # load page in wiki format, and return a Page object

class XHtmlLanguage(Language):
     """
     Properties and symbols/tags that exist in the xhtml language
     """
     def __init__(self):
         Language.__init__()
         self.list_tag = 'li'

     def load_page(self, ....):
         # load page in xhtml format, and return a Page object

For each language class that you have, you make a object (probably 1 for each 
language that you have). It describes what the language contains and how it 
performs its functions.
A Language object also knows how to load/save a page in its language, how to 
render it, etc.


Actual pages are stored in a Page object, I am not sure how this relates to 
your Format object.
(I don't understand the meaning of the name)


class Page(object):
     """
     A page of text
     """
     def __init__(self, lang):
	self.symbols = [] # Contents of the page, list or tree of Element's




class Element(object):
     """
     A concept that exists at a page (list, text, paragraph, picture, etc)
     """

class ListElement(Element):
     ....

class TextElement(Element):
     .....

A page is simply a tree or a list of Elements. Since a page here is 
language-agnostic, it doesn't even need to know its language.
(don't know whether this would work for you).


Hope it makes some sense,
Albert



More information about the Tutor mailing list