Re: storing meta data on dictionary keys

Discussion in 'Python' started by Erik Jones, Oct 10, 2007.

  1. Erik Jones

    Erik Jones Guest

    On Oct 9, 2007, at 7:37 PM, Andreas Kraemer wrote:

    > From: Chris Mellon <>
    > Sent: Tuesday, October 9, 2007 1:51:04 PM
    >
    > > Because, by definition, if you have the key then you don't need

    > to get
    > > it from the dict. What you're doing here is conflating 2 mappings

    > into
    > > one: string value->person and person->values. Use 2 explicit

    > dicts to
    > > make it clear what you're going, or use one dict and store a

    > tuple of
    > > values: person, friends = d["John"].

    >
    > No, that was not the point.
    >
    > I don't want the meta data to be used in the dictionary look-up,
    > and in fact I want to be able to modify or delete it later w/o
    > affecting dictionary look-up. In the toy example "John" and Str
    > ("John") are two different objects that map to the same value in
    > the dictionary, and "John" == Str("John") is True, since class Str
    > (str) inherits __hash__() and __eq__() from str. IOW, if John dyes
    > his hair, his friends shouldn't change :)
    >
    > Apart from this silly example I really encountered this issue when
    > using the networkx library. As I mentioned, graph nodes are stored
    > as dictionary keys there, i.e. nodes can be arbitrary objects with
    > the only requirement to be hashable, and I am stuck with this data
    > structure when using the library. In my example nodes are uniquely
    > identified by their name (a string) but may carry other attributes,
    > like their display color and shape, that are not used to identify a
    > node. Therefore, I thought, subclassing str would be the simplest,
    > most straightforward structure for a node object.
    >
    > Of course there are workarounds (e.g. get all keys with keys()),
    > but I thought something similar to a get_key() dictionary method
    > would be the easiest way to retrieve the actually stored key
    > object, and I was just surprised to discover that no such method
    > does exist ....


    So, do you not keep references to your nodes anywhere but the actual
    graph dict? I kind of agree with Chris here in that two dicts will
    work. One for the nodes, indexed by their strings. And, to use the
    actual nodes as keys simply override __hash__ in your Node object
    classes.

    >>> class Node(object):

    .... def __init__(self, name, **kwargs):
    .... self.name = name
    .... for k, v in kwargs.items():
    .... self.__dict__[k] = v
    ....
    .... def __hash__(self):
    .... return hash(self.name)
    ....
    >>> nodes = {}
    >>> graph = {}
    >>>
    >>> n = Node('foo')
    >>> m = Node('blah', baz=5)
    >>>
    >>> nodes[n.name] = n
    >>> nodes[m.name] = m
    >>>
    >>> for name, node in nodes.items():

    .... graph[node] = "whatever for node %s" % name
    ....
    >>> nodes{'blah': <__main__.Node object at 0x76c50>, 'foo':

    <__main__.Node object at 0x76d30>}
    >>> graph{<__main__.Node object at 0x76c50>: 'whatever for node

    blah', <__main__.Node object at 0x76d30>: 'whatever for node foo'}
    >>> graph[nodes['foo']]'whatever for node foo'


    Erik Jones

    Software Developer | Emma®

    800.595.4401 or 615.292.5888
    615.292.0777 (fax)

    Emma helps organizations everywhere communicate & market in style.
    Visit us online at http://www.myemma.com
     
    Erik Jones, Oct 10, 2007
    #1
    1. Advertising

  2. On Oct 9, 9:18 pm, Erik Jones <> wrote:
    > So, do you not keep references to your nodes anywhere but the actual
    > graph dict? I kind of agree with Chris here in that two dicts will
    > work. One for the nodes, indexed by their strings.


    Yes, I guess that's exactly what I want. To keep things as simple as
    possible and not having to keep track of another dictionary. If you
    look at class Dict(dict) in the OP, simulating the behavior I'd liked
    to have seen for built-in dict itself, the second dictionary is
    actually hidden so I don't have to bother with it any more ...

    > And, to use the
    > actual nodes as keys simply override __hash__ in your Node object
    > classes.


    The trivial "class Str(str): pass" in the OP (that already inherits
    the correct __hash__ and
    __eq__) serves the same purpose as your Node(object) below, except
    that self.name is stored
    in the str built-in, and there is no flashy initializer.

    >
    > >>> class Node(object):

    > ... def __init__(self, name, **kwargs):
    > ... self.name = name
    > ... for k, v in kwargs.items():
    > ... self.__dict__[k] = v
    > ...
    > ... def __hash__(self):
    > ... return hash(self.name)
    > ...
    > >>> nodes = {}
    > >>> graph = {}
    > >>>
    > >>> n = Node('foo')
    > >>> m = Node('blah', baz=5)
    > >>>
    > >>> nodes[n.name] = n
    > >>> nodes[m.name] = m
    > >>>
    > >>> for name, node in nodes.items():

    > ... graph[node] = "whatever for node %s" % name
    > ...
    > >>> nodes{'blah': <__main__.Node object at 0x76c50>, 'foo':

    > <__main__.Node object at 0x76d30>}
    > >>> graph{<__main__.Node object at 0x76c50>: 'whatever for node

    > blah', <__main__.Node object at 0x76d30>: 'whatever for node foo'}
    > >>> graph[nodes['foo']]'whatever for node foo'


    I still believe that it would be a nice-to-have, and probably only a
    small effort to equip
    the built-in dict with a get_key() method. The whole mechanism of
    dictionary look-up in Python,
    based on "duck typing", and only caring about the __hash__ and __eq__
    methods supplied by
    the key object, is constructed in a way that allows objects inserted
    into the dictionary as keys
    and objects used for look-up to be (non-trivially) distinct.
    Therefore, it would actually be
    nice to be able to retrieve keys from the dict in a direct way (other
    than via .keys()).
    The same may apply to sets and frozensets as well ...;-)
     
    Andreas Kraemer, Oct 11, 2007
    #2
    1. Advertising

  3. Erik Jones

    Erik Jones Guest

    On Oct 10, 2007, at 6:40 PM, Andreas Kraemer wrote:

    > On Oct 9, 9:18 pm, Erik Jones <> wrote:
    >> So, do you not keep references to your nodes anywhere but the actual
    >> graph dict? I kind of agree with Chris here in that two dicts will
    >> work. One for the nodes, indexed by their strings.

    >
    > Yes, I guess that's exactly what I want. To keep things as simple as
    > possible and not having to keep track of another dictionary. If you
    > look at class Dict(dict) in the OP, simulating the behavior I'd liked
    > to have seen for built-in dict itself, the second dictionary is
    > actually hidden so I don't have to bother with it any more ...


    If you're sure that 1. this use case won't grow and 2. that you'll
    only be the only person ever using code, then it's your choice of
    preference. Both of those points are equally important. 1 is a
    manageability issue in that you are complicating what could be an
    easy solution for a small gain in convenience -- one that could just
    as easily be had by encapsulating the management of both dicts in an
    explicit class or function/method. Let me quote lines 2 through 5 of
    The Zen of Python (PEP20):

    Explicit is better than implicit.
    Simple is better than complex.
    Complex is better than complicated.
    Flat is better than nested.

    Using two dicts would be explicit, simple and flat. Your's is none
    of those.

    For point 2, if anyone else needs to work with your code they're most
    likely going to be confused as the implementation is overly complex.
    I know when I initially read through it I had to read it a couple
    times to assure myself of what it was doing because I was thinking
    that it seems that there's more going on than there is, so there must
    be. Remember, the best solutions are those that not only you can
    understand, but the next guy/girl/?.

    >
    >> And, to use the
    >> actual nodes as keys simply override __hash__ in your Node object
    >> classes.

    >
    > The trivial "class Str(str): pass" in the OP (that already inherits
    > the correct __hash__ and
    > __eq__) serves the same purpose as your Node(object) below, except
    > that self.name is stored
    > in the str built-in, and there is no flashy initializer.


    True, but you're breaking the intended abstraction of inheriting from
    str, i.e. the reason you can inherit from str is so that you can
    specialize strings, not get __has__ and __eq__ for free. You can
    define those on your own for a reason.

    >
    >>
    >>>>> class Node(object):

    >> ... def __init__(self, name, **kwargs):
    >> ... self.name = name
    >> ... for k, v in kwargs.items():
    >> ... self.__dict__[k] = v
    >> ...
    >> ... def __hash__(self):
    >> ... return hash(self.name)
    >> ...
    >>>>> nodes = {}
    >>>>> graph = {}
    >>>>>
    >>>>> n = Node('foo')
    >>>>> m = Node('blah', baz=5)
    >>>>>
    >>>>> nodes[n.name] = n
    >>>>> nodes[m.name] = m
    >>>>>
    >>>>> for name, node in nodes.items():

    >> ... graph[node] = "whatever for node %s" % name
    >> ...
    >>>>> nodes{'blah': <__main__.Node object at 0x76c50>, 'foo':

    >> <__main__.Node object at 0x76d30>}
    >>>>> graph{<__main__.Node object at 0x76c50>: 'whatever for node

    >> blah', <__main__.Node object at 0x76d30>: 'whatever for node foo'}
    >>>>> graph[nodes['foo']]'whatever for node foo'

    >
    > I still believe that it would be a nice-to-have, and probably only a
    > small effort to equip
    > the built-in dict with a get_key() method. The whole mechanism of
    > dictionary look-up in Python,
    > based on "duck typing", and only caring about the __hash__ and __eq__
    > methods supplied by
    > the key object, is constructed in a way that allows objects inserted
    > into the dictionary as keys
    > and objects used for look-up to be (non-trivially) distinct.
    > Therefore, it would actually be
    > nice to be able to retrieve keys from the dict in a direct way (other
    > than via .keys()).
    > The same may apply to sets and frozensets as well ...;-)


    So, (explicitly:) you want the built-in dict to be a dictionary that
    also maintains a dictionary of its own keys? Not likely to happen.
    Again, you're asking to have one dictionary act as two and that's
    just confusing. I'm not saying that get_key is a bad idea for
    (possibly) some use cases, just that what you're actually doing is
    creating a dictionary wherein the same key can return two different
    object depending on the retrieval method used and that is NOT what
    the built-in dict is for. In fact, now that I think of it, get_key
    is probably a bad name for it, get_other_object_with_this_same_key is
    probably more apt :)

    Erik Jones

    Software Developer | Emma®

    800.595.4401 or 615.292.5888
    615.292.0777 (fax)

    Emma helps organizations everywhere communicate & market in style.
    Visit us online at http://www.myemma.com
     
    Erik Jones, Oct 11, 2007
    #3
  4. On Oct 10, 9:00 pm, Erik Jones <> wrote:
    > If you're sure that 1. this use case won't grow and 2. that you'll
    > only be the only person ever using code, then it's your choice of
    > preference. Both of those points are equally important. 1 is a
    > manageability issue in that you are complicating what could be an
    > easy solution for a small gain in convenience -- one that could just
    > as easily be had by encapsulating the management of both dicts in an
    > explicit class or function/method. Let me quote lines 2 through 5 of
    > The Zen of Python (PEP20):
    >
    > Explicit is better than implicit.
    > Simple is better than complex.
    > Complex is better than complicated.
    > Flat is better than nested.
    >
    > Using two dicts would be explicit, simple and flat. Your's is none
    > of those.
    >
    > For point 2, if anyone else needs to work with your code they're most
    > likely going to be confused as the implementation is overly complex.
    > I know when I initially read through it I had to read it a couple
    > times to assure myself of what it was doing because I was thinking
    > that it seems that there's more going on than there is, so there must
    > be. Remember, the best solutions are those that not only you can
    > understand, but the next guy/girl/?.


    You are right, and these are all very valid points. One of the reasons
    I like Python so much is the philosophy behind it. Nevertheless,
    everything is relative, so a pattern that seems complex (or unusual)
    at first may become simple when people get used to it ... I hope, I
    may be excused. In the organization where I work I am mainly
    prototyping (more on the "scientific/algorithm" side) and not required
    to write maintainable code. The engineers will do that (in Java
    anyway, sadly ..).

    > > The trivial "class Str(str): pass" in the OP (that already inherits
    > > the correct __hash__ and
    > > __eq__) serves the same purpose as your Node(object) below, ...

    >
    > True, but you're breaking the intended abstraction of inheriting from
    > str, i.e. the reason you can inherit from str is so that you can
    > specialize strings, not get __has__ and __eq__ for free. You can
    > define those on your own for a reason.


    Who says that that's the intended abstraction of inheriting from str?
    I thought that one purpose of inheritance was the reuse of behavior
    (i.e. methods) (and maybe state) of the superclass. So, my class
    Str(str): pass behaves like a string in all aspects, except that you
    can attach some meta data that won't affect that behavior. In fact, I
    use that pattern quite frequently also for other built-in immutable
    types, e.g. floats or ints, which allows me to very easily "tag"
    numerical data (e.g. as an outlier in a stats application or whatever)
    without affecting any subsequent code operating on it as long as it
    only uses its built-in "flavor". This is the power of Python's duck
    typing!

    > So, (explicitly:) you want the built-in dict to be a dictionary that
    > also maintains a dictionary of its own keys?


    Without knowing the internals, so I may be wrong. But dict must
    somehow maintain a dictionary of the key objects already. How else
    could it detect collisions of hash values, when it must use __eq__ to
    determine equality of the "query" key object to one of the inserted
    keys having the same hash value? It also needs to do the same check
    when overwriting an existing key.

    > Not likely to happen.
    > Again, you're asking to have one dictionary act as two and that's
    > just confusing. I'm not saying that get_key is a bad idea for
    > (possibly) some use cases, just that what you're actually doing is
    > creating a dictionary wherein the same key can return two different
    > object depending on the retrieval method used and that is NOT what
    > the built-in dict is for. In fact, now that I think of it, get_key
    > is probably a bad name for it, get_other_object_with_this_same_key is
    > probably more apt :)


    Or more precise:
    get_key_that_was_used_when_value_was_inserted_into_dictionary :)

    Thanks, for taking the time for this elaborate response!

    Cheers,
    Andreas
     
    Andreas Kraemer, Oct 11, 2007
    #4
  5. > > [...] In fact, now that I think of it, get_key
    > > is probably a bad name for it, get_other_object_with_this_same_key is
    > > probably more apt :)

    >
    > Or more precise:
    > get_key_that_was_used_when_value_was_inserted_into_dictionary :)


    Or even more precisely:

    get_key_object_that_was_used_when_the_initial_value_belonging_to_that_key_was_inserted_into_dictionary

    since rebinding other values to the stored key won't change it:

    class Str(str):
    def __repr__(self):
    return str(self)+': '+self.tag

    >>> a = Str('foo')
    >>> a.tag = "I'm the first one!"
    >>> b = Str('foo')
    >>> b.tag = "I'm not."
    >>> d = {}
    >>> d[a] = 'bar'
    >>> d = 'spam'
    >>> d.items()[0]

    (foo: I'm the first one!, 'spam')
     
    Andreas Kraemer, Oct 11, 2007
    #5
  6. Erik Jones

    Erik Jones Guest

    On Oct 11, 2007, at 1:36 AM, Andreas Kraemer wrote:

    > On Oct 10, 9:00 pm, Erik Jones <> wrote:
    >> If you're sure that 1. this use case won't grow and 2. that you'll
    >> only be the only person ever using code, then it's your choice of
    >> preference. Both of those points are equally important. 1 is a
    >> manageability issue in that you are complicating what could be an
    >> easy solution for a small gain in convenience -- one that could just
    >> as easily be had by encapsulating the management of both dicts in an
    >> explicit class or function/method. Let me quote lines 2 through 5 of
    >> The Zen of Python (PEP20):
    >>
    >> Explicit is better than implicit.
    >> Simple is better than complex.
    >> Complex is better than complicated.
    >> Flat is better than nested.
    >>
    >> Using two dicts would be explicit, simple and flat. Your's is none
    >> of those.
    >>
    >> For point 2, if anyone else needs to work with your code they're most
    >> likely going to be confused as the implementation is overly complex.
    >> I know when I initially read through it I had to read it a couple
    >> times to assure myself of what it was doing because I was thinking
    >> that it seems that there's more going on than there is, so there must
    >> be. Remember, the best solutions are those that not only you can
    >> understand, but the next guy/girl/?.

    >
    > You are right, and these are all very valid points. One of the reasons
    > I like Python so much is the philosophy behind it. Nevertheless,
    > everything is relative, so a pattern that seems complex (or unusual)
    > at first may become simple when people get used to it ... I hope, I
    > may be excused. In the organization where I work I am mainly
    > prototyping (more on the "scientific/algorithm" side) and not required
    > to write maintainable code. The engineers will do that (in Java
    > anyway, sadly ..).


    Excellent. I'm actually love "hackish" approaches to things when you
    can afford to use them and it seems like you're in that situation.
    >
    >>> The trivial "class Str(str): pass" in the OP (that already inherits
    >>> the correct __hash__ and
    >>> __eq__) serves the same purpose as your Node(object) below, ...

    >>
    >> True, but you're breaking the intended abstraction of inheriting from
    >> str, i.e. the reason you can inherit from str is so that you can
    >> specialize strings, not get __has__ and __eq__ for free. You can
    >> define those on your own for a reason.

    >
    > Who says that that's the intended abstraction of inheriting from str?
    > I thought that one purpose of inheritance was the reuse of behavior
    > (i.e. methods) (and maybe state) of the superclass. So, my class
    > Str(str): pass behaves like a string in all aspects, except that you
    > can attach some meta data that won't affect that behavior. In fact, I
    > use that pattern quite frequently also for other built-in immutable
    > types, e.g. floats or ints, which allows me to very easily "tag"
    > numerical data (e.g. as an outlier in a stats application or whatever)
    > without affecting any subsequent code operating on it as long as it
    > only uses its built-in "flavor". This is the power of Python's duck
    > typing!


    No, duck typing and inheritance are two different things. Duck
    typing is when you implement the same operations as another object or
    class, whereas with inheritance you get the same implementation as
    that of the parent class. With duck typing, an object "acts like"
    another object in specific contexts. With inheritance, an object is
    the same general type as another object. You don't want string
    objects, you want objects that, in a specific case, act like strings
    but in others, I'm sure, not. How does a concept like string
    substitution fit with your inherited objects?

    >> So, (explicitly:) you want the built-in dict to be a dictionary that
    >> also maintains a dictionary of its own key

    >
    > Without knowing the internals, so I may be wrong. But dict must
    > somehow maintain a dictionary of the key objects already. How else
    > could it detect collisions of hash values, when it must use __eq__ to
    > determine equality of the "query" key object to one of the inserted
    > keys having the same hash value? It also needs to do the same check
    > when overwriting an existing key.


    If you can, get a copy of Beautiful Code (easily had from Amazon or
    the likes). It contains an chapter on Python's dictionary
    implementation.

    >> Not likely to happen.
    >> Again, you're asking to have one dictionary act as two and that's
    >> just confusing. I'm not saying that get_key is a bad idea for
    >> (possibly) some use cases, just that what you're actually doing is
    >> creating a dictionary wherein the same key can return two different
    >> object depending on the retrieval method used and that is NOT what
    >> the built-in dict is for. In fact, now that I think of it, get_key
    >> is probably a bad name for it, get_other_object_with_this_same_key is
    >> probably more apt :)

    >
    > Or more precise:
    > get_key_that_was_used_when_value_was_inserted_into_dictionary :)


    I like it!


    Erik Jones

    Software Developer | Emma®

    800.595.4401 or 615.292.5888
    615.292.0777 (fax)

    Emma helps organizations everywhere communicate & market in style.
    Visit us online at http://www.myemma.com
     
    Erik Jones, Oct 11, 2007
    #6
  7. On Oct 11, 10:17 am, Erik Jones <> wrote:

    > No, duck typing and inheritance are two different things. Duck
    > typing is when you implement the same operations as another object or
    > class, whereas with inheritance you get the same implementation as
    > that of the parent class.


    Except when you override a method ...

    > With duck typing, an object "acts like"
    > another object in specific contexts.


    That's exactly what Str(str) does in the context dictionary look-
    up ...

    > With inheritance, an object is
    > the same general type as another object. You don't want string
    > objects, you want objects that, in a specific case, act like strings
    > but in others, I'm sure, not. How does a concept like string
    > substitution fit with your inherited objects?


    Since Str(str) inherits all methods of str, those methods that return
    other strings (always new objects since str is immutable) like e.g.
    replace() will return str and not Str(str), and don't have the meta
    data copied. Which may be a bug or a feature ...:)

    I think the subtle difference here lies between the definition of
    inheritance and duck-typing, and how both are typically used in
    practice ...

    > If you can, get a copy of Beautiful Code (easily had from Amazon or
    > the likes). It contains an chapter on Python's dictionary
    > implementation.


    Thanks for the pointer. I like to look at beautiful code.
     
    Andreas Kraemer, Oct 11, 2007
    #7
  8. Erik Jones

    Erik Jones Guest

    On Oct 11, 2007, at 2:25 PM, Andreas Kraemer wrote:

    > On Oct 11, 10:17 am, Erik Jones <> wrote:
    >
    >> No, duck typing and inheritance are two different things. Duck
    >> typing is when you implement the same operations as another object or
    >> class, whereas with inheritance you get the same implementation as
    >> that of the parent class.

    >
    > Except when you override a method ...


    Right. But, that's specialization which is still part of the
    inheritance model, and not duck typing, since you're also inheriting
    state. Duck typing is about implementing an interface and has
    nothing to do with state. Put another way, with inheritance you're
    getting both behaviour and state, with duck typing you're mimicking
    behaviour.

    >
    >> With duck typing, an object "acts like"
    >> another object in specific contexts.

    >
    > That's exactly what Str(str) does in the context dictionary look-
    > up ...


    No, in that case Str "is-a" str, it doesn't have to act like a str
    because it is one, whereas with my Node example, Node "acts-like" a
    str (or int or any other hashable). Read the first sentence of the
    Wikipedia article on duck typing: http://en.wikipedia.org/wiki/
    Duck_typing

    >> With inheritance, an object is
    >> the same general type as another object. You don't want string
    >> objects, you want objects that, in a specific case, act like strings
    >> but in others, I'm sure, not. How does a concept like string
    >> substitution fit with your inherited objects?

    >
    > Since Str(str) inherits all methods of str, those methods that return
    > other strings (always new objects since str is immutable) like e.g.
    > replace() will return str and not Str(str), and don't have the meta
    > data copied. Which may be a bug or a feature ...:)
    >
    > I think the subtle difference here lies between the definition of
    > inheritance and duck-typing, and how both are typically used in
    > practice ...


    Right, here you're wanting to implement your Node as a specialization
    of str in order to get only some of it's behaviour. But the
    abstractions of a graph node and string don't mix with some of str's
    other behaviour. To me this is obtuse:

    >>> class Node(str): pass

    ....
    >>> n = Node('funky %s')
    >>> n.children = [Node('child1'), Node('child2')]
    >>> print n % 'yes

    funky yes

    Duck typing is what you're looking for and is specifically not
    inheritance.

    I'd like to note that my entire argument has been based on the
    semantics of inheritance v. duck typing. I'm not saying what you're
    doing won't work, just that when I see someone inherit from str I
    expect a str-like object in intention, it being hashable should be
    incidental to that. This example is sufficiently small and simple
    that if you do choose inheritance you're not likely to get bitten.
    But, once you get in the habit of breaking abstractions like this you
    open yourself up for a world of hurt in other scenarios.

    Another thing to think about is that the only thing you have to gain
    with piggy backing str like this is saving three lines of typing, 4
    lines to implement __hash__ and __eq__ v. 1 line to inherit from str.

    Erik Jones

    Software Developer | Emma®

    800.595.4401 or 615.292.5888
    615.292.0777 (fax)

    Emma helps organizations everywhere communicate & market in style.
    Visit us online at http://www.myemma.com
     
    Erik Jones, Oct 11, 2007
    #8
  9. On Oct 11, 1:42 pm, Erik Jones <> wrote:
    > On Oct 11, 2007, at 2:25 PM, Andreas Kraemer wrote:
    >
    > > On Oct 11, 10:17 am, Erik Jones <> wrote:

    >
    > >> No, duck typing and inheritance are two different things. Duck
    > >> typing is when you implement the same operations as another object or
    > >> class, whereas with inheritance you get the same implementation as
    > >> that of the parent class.

    >
    > > Except when you override a method ...

    >
    > Right. But, that's specialization which is still part of the
    > inheritance model, and not duck typing, since you're also inheriting
    > state. Duck typing is about implementing an interface and has
    > nothing to do with state. Put another way, with inheritance you're
    > getting both behaviour and state, with duck typing you're mimicking
    > behaviour.
    >
    >
    >
    > >> With duck typing, an object "acts like"
    > >> another object in specific contexts.

    >
    > > That's exactly what Str(str) does in the context dictionary look-
    > > up ...

    >
    > No, in that case Str "is-a" str, it doesn't have to act like a str
    > because it is one, whereas with my Node example, Node "acts-like" a
    > str (or int or any other hashable). Read the first sentence of the
    > Wikipedia article on duck typing:http://en.wikipedia.org/wiki/
    > Duck_typing
    >
    > >> With inheritance, an object is
    > >> the same general type as another object. You don't want string
    > >> objects, you want objects that, in a specific case, act like strings
    > >> but in others, I'm sure, not. How does a concept like string
    > >> substitution fit with your inherited objects?

    >
    > > Since Str(str) inherits all methods of str, those methods that return
    > > other strings (always new objects since str is immutable) like e.g.
    > > replace() will return str and not Str(str), and don't have the meta
    > > data copied. Which may be a bug or a feature ...:)

    >
    > > I think the subtle difference here lies between the definition of
    > > inheritance and duck-typing, and how both are typically used in
    > > practice ...

    >
    > Right, here you're wanting to implement your Node as a specialization
    > of str in order to get only some of it's behaviour. But the
    > abstractions of a graph node and string don't mix with some of str's
    > other behaviour. To me this is obtuse:
    >
    > >>> class Node(str): pass

    > ...
    > >>> n = Node('funky %s')
    > >>> n.children = [Node('child1'), Node('child2')]
    > >>> print n % 'yes

    > funky yes
    >
    > Duck typing is what you're looking for and is specifically not
    > inheritance.
    >
    > I'd like to note that my entire argument has been based on the
    > semantics of inheritance v. duck typing. I'm not saying what you're
    > doing won't work, just that when I see someone inherit from str I
    > expect a str-like object in intention, it being hashable should be
    > incidental to that. This example is sufficiently small and simple
    > that if you do choose inheritance you're not likely to get bitten.
    > But, once you get in the habit of breaking abstractions like this you
    > open yourself up for a world of hurt in other scenarios.
    >
    > Another thing to think about is that the only thing you have to gain
    > with piggy backing str like this is saving three lines of typing, 4
    > lines to implement __hash__ and __eq__ v. 1 line to inherit from str.


    I get your point. And you are right, I certainly won't use this
    technique for anything larger than in this example. As I mentioned in
    my OP, I was using the networkx graph package (BTW an excellent
    library IMHO, https://networkx.lanl.gov/wiki), and in most cases it is
    sufficient (as in the examples on the web site) that nodes *are* just
    simple strings or integers. Networkx is about the graph (i.e.
    relationships between nodes) and does not use any "internal" node
    information except for their identity as defined by __hash__ and
    __eq__. That's from where I started ....

    Andreas
     
    Andreas Kraemer, Oct 11, 2007
    #9
    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. Nym Pseudo

    META NAME and META HTTP-EQUIV

    Nym Pseudo, Sep 26, 2003, in forum: HTML
    Replies:
    1
    Views:
    611
    =?iso-8859-1?Q?brucie?=
    Sep 26, 2003
  2. flamesrock
    Replies:
    2
    Views:
    402
    flamesrock
    Mar 12, 2005
  3. Duane Johnson

    Meta methods to govern meta data?

    Duane Johnson, Oct 25, 2005, in forum: Ruby
    Replies:
    6
    Views:
    274
    Adam Sanderson
    Oct 28, 2005
  4. Erik Veenstra

    Meta-Meta-Programming

    Erik Veenstra, Feb 7, 2006, in forum: Ruby
    Replies:
    29
    Views:
    446
    Erik Veenstra
    Feb 8, 2006
  5. Erik Veenstra

    Meta-Meta-Programming, revisited

    Erik Veenstra, Jul 21, 2006, in forum: Ruby
    Replies:
    21
    Views:
    487
    Erik Veenstra
    Jul 25, 2006
Loading...

Share This Page