[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