Re: looking for design pattern name

Discussion in 'Python' started by Raymond Hettinger, Jul 30, 2003.

  1. "Andrew Dalke" <> wrote in message
    news:bg1lt6$81g$...
    > I'm looking for the name used for a pattern like what Qt uses for
    > it's container hierarchy. I'm doing this design for molecules, so I'll
    > use that as my example.
    >
    > There's a container, in this case, a Molecule. A Molecule has Atoms
    > and Bonds. When an Atom, it is passed the parent container as
    > part of the constructor, as in
    >
    > mol = Molecule()
    > O1 = Atom(mol, 8) # note the parent in the constructor
    > O2 = Atom(mol, 8)
    > Bond(O1, O2, 2) # (the parent is implicit - comes from the atoms)


    What happens in Bond() if O1 and O2 have different parents?

    When O1 is constructed, does mol get informed? IOW, after the
    four assignments, does mol know that it has two atoms and double bond?


    > I want to compare this to a design like
    >
    > mol = Molecule()
    > O1 = mol.add_atom(8)
    > O2 = mol.add_atom(8)
    > mol.add_bond(O1, O2)
    >
    > which does not allow a user-derived Atom and Bond class
    >
    > and one like
    >
    > mol = Molecule()
    > O1 = Atom(8)
    > mol.add_atom(O1)
    > O2 = Atom(8)
    > mol.add_atom(O2)
    > b = Bond(2)
    > mol.add_bond(b)


    How does this setup handle:

    mol1 = Molecule()
    mol2 = Molecule()
    b = Bond(2)
    mol1.add_bond(b)
    mol2.add_bond(b)

    Is there any way to detect that the same Atom or Bond
    being used in two different molecules?



    >
    > Anyone have good names for any of these three styles?


    Assuming that the first approach operates by constructing
    components and notifying parents that it is a new component,
    then I would call it the Widget pattern because it operates like
    Tk widgets:

    root = Tk() # root = Molecule()
    b1 = Button(root) # b1 = Atom(root, 8)
    b2 = Button(root) # b2 = Atom(root, 8)

    In Chapter 2 of The Art of Objects, the second and third
    approaches are called Collection Manager and Container.
    The key distinction is that a Container only contains the
    objects and is not responsible for creating them.

    Another idea is to name the patterns after the limitations
    inherent in each design. When Atoms are constructed
    implicitly, the design does not favor applications
    that break-up a molecule and re-attach some of the existing
    atoms into new molecules, so call it NoMix or some such.

    Still another ideas is to describe a pattern by its
    characteristic variant or invariant properties:

    * The first approach has components with fixed parents
    established at the time of component creation. Call the pattern
    SignUp since every new atom or bond has to immediately
    signup with an existing parent.

    * The second approach has captive components.
    This suggests the name MoleculeDriven to cover the idea
    that all component atoms or bonds are created from the
    molecule, thus assuring there can be no orphan atoms or bonds.

    * The third approach allows free mixing of components.
    This is suggestive of the name TinkerToy where all the
    molecular model components can lay separately on a
    table (sticks for bonds and cogwheels for atoms).



    Raymond Hettinger
     
    Raymond Hettinger, Jul 30, 2003
    #1
    1. Advertising

  2. Raymond Hettinger

    Andrew Dalke Guest

    Raymond Hettinger:
    > What happens in Bond() if O1 and O2 have different parents?


    Raises an exception - TypeError seems best. Here's the code

    class BaseBond:
    def __init__(self, atom1, atom2):
    parent = atom1.parent
    if parent is not atom2.parent:
    raise TypeError("atoms have different parents")
    if atom1 is atom2:
    raise TypeError("cannot bond an atom to itself")
    for b, xatom in atom1.xiter():
    if xatom is atom2:
    raise TypeError("bond already exists")

    self.parent = parent
    self.atoms = ImmutableSet([atom1, atom2])

    parent._add_bond(self, atom1, atom2)



    > When O1 is constructed, does mol get informed? IOW, after the
    > four assignments, does mol know that it has two atoms and double bond?


    Yes. See http://mgldev.scripps.edu/CRBM/Members/dalke and more
    specifically
    http://mgldev.scripps.edu/CRBM/Members/dalke/API-variations-2003-07-23
    (where the above code came from). Here's one possible Atom constructor

    class BaseAtom:
    def __init__(self, parent):
    self.parent = parent
    self.bonds = Set()
    parent._add_atom(self)

    In both the Atom and Bond, the object sets up its own data, then tells
    the Molecule that it exists. The Molecule tells the other objects about
    the new instance.


    > How does this setup handle:
    >
    > mol1 = Molecule()
    > mol2 = Molecule()
    > b = Bond(2)
    > mol1.add_bond(b)
    > mol2.add_bond(b)


    (Oops! My original code forgot to tell which atoms the bond code to.)

    Presumably the add_bond method sets a "parent" instance in actual bond.
    That means when the bond is created, bond.parent is None, then after
    mol1.add_bond(b), bond.parent is mol1 and trying to do a mol2.add_bond(b)
    will raise an exception because bond.parent is not None and bond.parent
    is not b1.

    > Is there any way to detect that the same Atom or Bond
    > being used in two different molecules?


    There must be, otherwise there get to be all sorts of bizarre
    behaviours. The best approach seems to be using a 'parent'
    attribute in the Atom/Bond. My "parent in constructor" approach
    means that obj.parent can never be None, which I like. The
    atom.setParent(mol) or mol.addAtom(atom) approch, where
    container assignment is done after construction, seems prefered
    by some.

    > Assuming that the first approach operates by constructing
    > components and notifying parents that it is a new component,
    > then I would call it the Widget pattern because it operates like
    > Tk widgets:
    >
    > root = Tk() # root = Molecule()
    > b1 = Button(root) # b1 = Atom(root, 8)
    > b2 = Button(root) # b2 = Atom(root, 8)


    Ahh, year ago I tried but failed to understand Tk's packing, so
    it wasn't until last year when I looked at Qt that I saw this idiom.

    'Widget'. Hmmm, okay. I don't think of atoms and bonds as
    widgets, but I understand the comparison.

    > In Chapter 2 of The Art of Objects, the second and third
    > approaches are called Collection Manager and Container.
    > The key distinction is that a Container only contains the
    > objects and is not responsible for creating them.


    I'll see if I can dig up a copy of the book. Thanks for the
    pointer.

    > Another idea is to name the patterns after the limitations
    > inherent in each design. When Atoms are constructed
    > implicitly, the design does not favor applications
    > that break-up a molecule and re-attach some of the existing
    > atoms into new molecules, so call it NoMix or some such.


    "Ahh! now see the violence inherent in the system!"
    http://bau2.uibk.ac.at/sg/python/Sounds/HolyGrail.wav/violence.wav
    (Sorry, but this *is* a Python newsgroup. :)

    Yeah, I was thinking about that limitation yesterday. There are
    times when you want to combine two existing structure (eg, adding
    waters to a system, or embedding a protein in a membrane) and
    since both systems have a different parent it's tricky.

    On the other hand, most changes are more complicated than that.
    That is, most times people merge to Molecules is not simply to
    combine the two sets of component graphs. Usually the merger
    is to modify a component of the existing graph.

    For example, proteins are made of chains of amino acids. An
    amino acid looks like

    sidechain
    |
    C
    / \
    amide end [NH3+] C--[O-] carboxyl end
    ||
    O

    When two amino acids are merged to from a short protein, they start
    like this

    sidechain
    |
    C O
    / \ ||
    [NH3+] C--[O-] [NH3+] C--[O-]
    || \ /
    O C
    |
    sidechain

    One of the carboxyl oxygens (btw, they are symmetric.
    I just drew one of the resonant states) and two of the
    hydrogens from the amide combine to form water. The
    plus and minus charges cancel out, and a bond forms
    between the C and the N.

    sidechain
    |
    C H O
    / \ | ||
    [NH3+] C--O--N C--[O-] + H-O-H
    || \ /
    O C
    |
    sidechain

    People will want to assemble proteins using the lower-level
    graph operations. An obvious way is to supply fragments
    as templates and some operations to join them. Eg,

    ALA + GLY + PRO + ALA

    makes a chain of 4 residues. But this is complicated because
    the "+" operation needs to know how to modify the two terminals
    to do the join and to get rid of the newly created water.

    (Another way is to make the sequence as string then
    convert it to a graph, as in: make_molecule("AGPA"). But
    it would be nice to build an amino acid as "peptide backbone"
    + "template for the side chain."... except that doesn't work
    well for proline. Hmmm.)

    Err, I tangented, didn't I.

    So, joining molecules is going to be tricky because it isn't
    likely a simple graph merge, it will require editing the two
    graphs. And because I want templates, I need to copy
    an existing graph template before editing it, which means
    the NoMix idea isn't that descriptive.

    Guess I should write up some use cases, eh?

    > Still another ideas is to describe a pattern by its
    > characteristic variant or invariant properties:


    Sounds like the best approach, given that there doesn't
    seem to be a well established name and the 'Widget'
    name you suggested is from a too distant domain.

    Thanks for all your help!

    Andrew
     
    Andrew Dalke, Jul 30, 2003
    #2
    1. Advertising

  3. Raymond Hettinger

    John J. Lee Guest

    "Andrew Dalke" <> writes:

    > Raymond Hettinger:
    > > What happens in Bond() if O1 and O2 have different parents?

    >
    > Raises an exception - TypeError seems best. Here's the code

    [...]

    Yuck. Doesn't the standard library reserve TypeError for occasions
    where the, um, type of an argument is not appropriate? 'Type' does
    generally means the interface rather than type(object), but that's
    quite different to what you're checking. Checking whether atoms have
    different parents surely isn't a type check. Why not ValueError or
    your own exception (possibly derived from ValueError)?

    [.........]
    > Err, I tangented, didn't I.

    [...]

    "Verbing weirds language", to quote Calvin.

    :)


    John
     
    John J. Lee, Jul 31, 2003
    #3
  4. Raymond Hettinger

    Andrew Dalke Guest

    John J. Lee:
    > Yuck. Doesn't the standard library reserve TypeError for occasions
    > where the, um, type of an argument is not appropriate?


    Quoting from the docs,
    exception TypeError
    Raised when a built-in operation or function is applied to an object of
    inappropriate type. The associated value is a string giving details about
    the type mismatch.

    In addition to your comments, this is not a built-in operation, so it's
    the wrong exception.

    > Why not ValueError or
    > your own exception (possibly derived from ValueError)?


    exception ValueError
    Raised when a built-in operation or function receives an argument that
    has the right type but an inappropriate value, and the situation is not
    described by a more precise exception such as IndexError.

    Yup, seems reasonable. I'll change. Thanks!

    See you at the next Calvinball tournament, Spaceman Lee.

    Andrew
     
    Andrew Dalke, Jul 31, 2003
    #4
  5. Raymond Hettinger

    Ben Finney Guest

    On 31 Jul 2003 19:17:49 +0100, John J. Lee wrote:
    > "Andrew Dalke" <> writes:
    >> Err, I tangented, didn't I.

    >
    > "Verbing weirds language", to quote Calvin.


    From my sigmonster:

    "First they came for the verbs, and I said nothing, for verbing
    weirds language. Then, they arrival for the nouns and I speech
    nothing, for I no verbs." -- Peter Ellis

    --
    \ "I'm beginning to think that life is just one long Yoko Ono |
    `\ album; no rhyme or reason, just a lot of incoherent shrieks and |
    _o__) then it's over." -- Ian Wolff |
    Ben Finney <http://bignose.squidly.org/>
     
    Ben Finney, Jul 31, 2003
    #5
  6. Raymond Hettinger

    John J. Lee Guest

    "Andrew Dalke" <> writes:
    [...]
    > Quoting from the docs,
    > exception TypeError
    > Raised when a built-in operation or function is applied to an object of
    > inappropriate type. The associated value is a string giving details about
    > the type mismatch.
    >
    > In addition to your comments, this is not a built-in operation, so it's
    > the wrong exception.

    [...]

    Hmm, I don't think that's what the docs meant to communicate -- you're
    allowed to use it in your non-builtin code. At least, that's what
    I've always assumed:


    def isstringlike(x):
    try: x+""
    except: return False
    else: return True

    class Foo:
    def __init__(self, stringy):
    if not isstringlike(stringy):
    raise TypeError("stringy must be string-like")


    I don't see anything in that documentation that says that's bad.


    John
     
    John J. Lee, Aug 3, 2003
    #6
  7. John J. Lee wrote:

    > "Andrew Dalke" <> writes:
    > [...]
    >> Quoting from the docs,
    >> exception TypeError
    >> Raised when a built-in operation or function is applied to an object
    >> of inappropriate type. The associated value is a string giving details
    >> about the type mismatch.
    >>
    >> In addition to your comments, this is not a built-in operation, so it's
    >> the wrong exception.

    > [...]
    >
    > Hmm, I don't think that's what the docs meant to communicate -- you're
    > allowed to use it in your non-builtin code. At least, that's what
    > I've always assumed:


    I agree with your interpretation. Lots of NON-builtin code that is
    GvR-approved, e.g. throughout the Python libraries, happily raises such
    exceptions as TypeError, so it does seem that the above-quoted para
    must NOT be read as "Raised ONLY when a built-in" (etc) but rather
    as "Raised (among other circumstances) when a built-in" (etc).


    Alex
     
    Alex Martelli, Aug 3, 2003
    #7
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Steve
    Replies:
    3
    Views:
    324
  2. sunny
    Replies:
    1
    Views:
    467
    Salt_Peter
    Dec 7, 2006
  3. Pallav singh
    Replies:
    0
    Views:
    364
    Pallav singh
    Jan 22, 2012
  4. Pallav singh
    Replies:
    0
    Views:
    405
    Pallav singh
    Jan 22, 2012
  5. Pallav singh
    Replies:
    1
    Views:
    453
    Peter Remmers
    Jan 22, 2012
Loading...

Share This Page