python bijection

Discussion in 'Python' started by Joshua Bronson, Nov 19, 2009.

  1. I couldn't find a library providing a bijective map data structure
    (allowing for constant-time lookups by value) in the few minutes I
    looked, so I took a few more minutes to code one up:
    http://bitbucket.org/jab/toys/src/tip/bijection.py

    Is this at all worth releasing? Comments and suggestions welcome.

    Josh
     
    Joshua Bronson, Nov 19, 2009
    #1
    1. Advertising

  2. On Thu, 19 Nov 2009 15:24:46 -0800, Joshua Bronson wrote:

    > I couldn't find a library providing a bijective map data structure
    > (allowing for constant-time lookups by value) in the few minutes I
    > looked, so I took a few more minutes to code one up:
    > http://bitbucket.org/jab/toys/src/tip/bijection.py
    >
    > Is this at all worth releasing?



    You just did :)


    > Comments and suggestions welcome.


    If I want a mapping a <-> b, I generally just create a dict {a:b, b:a}.
    What is the advantages or disadvantages of your code over the simplicity
    of the dict approach?

    (That is, sell us on the features of your approach.)



    --
    Steven
     
    Steven D'Aprano, Nov 20, 2009
    #2
    1. Advertising

  3. On Nov 19, 7:05 pm, Steven D'Aprano <st...@REMOVE-THIS-
    cybersource.com.au> wrote:
    > If I want a mapping a <-> b, I generally just create a dict {a:b, b:a}.
    > What is the advantages or disadvantages of your code over the simplicity
    > of the dict approach?


    Well for one, you don't have to manually update the mapping from b ->
    a if ever the mapping from a -> b changes. With your method you have
    to write something like "d[a] = c; d[c] = a; del d" instead of just
    "d[a] = c", "del d[d.pop(a)]" instead of just "del d[a]", etc.

    More significantly, your approach doesn't actually model a bijection
    since there's no distinction between keys (the domain) and values (the
    range). In other words, you lose information about which way is the
    forward mapping and which is the inverse mapping. Worse, d.keys() and
    d.values() would each give you the combination of your keys and
    values, neither of which would be right, and d.items() would also
    return twice as many elements as you expect with no way to distinguish
    which side of the mapping a given pair comes from.
     
    Joshua Bronson, Nov 20, 2009
    #3
  4. Joshua Bronson

    Carl Banks Guest

    On Nov 19, 3:24 pm, Joshua Bronson <> wrote:
    > I couldn't find a library providing a bijective map data structure
    > (allowing for constant-time lookups by value) in the few minutes I
    > looked, so I took a few more minutes to code one up:http://bitbucket.org/jab/toys/src/tip/bijection.py
    >
    > Is this at all worth releasing? Comments and suggestions welcome.



    Apart from the GPL, it seems perfectly fine to release, and looks like
    an interesting strategy. I've wanted one of those once in a while,
    never enough to bother looking for one or writing one myself.

    But you should absolutely not inherit from dict if you're overriding
    all it's methods. It's useless and wasteful to do that, perhaps
    dangerous. You end up using bytes for a small hash table that's never
    used.

    Plus Python 3 has a notion of Abstract Base Classes: it will allow
    customization of isinstance to advertise that your class implements
    MutableMapping, which is the right way to do it.


    Carl Banks
     
    Carl Banks, Nov 20, 2009
    #4
  5. On Nov 19, 9:17 pm, Carl Banks <> wrote:
    > Apart from the GPL


    what Ben said :)

    > it seems perfectly fine to release, and looks like
    > an interesting strategy. I've wanted one of those once in a while,
    > never enough to bother looking for one or writing one myself.


    glad to hear it! i'll release it to pypi if such feedback continues.

    > But you should absolutely not inherit from dict if you're overriding
    > all it's methods. It's useless and wasteful to do that, perhaps
    > dangerous. You end up using bytes for a small hash table that's never
    > used.
    >
    > Plus Python 3 has a notion of Abstract Base Classes: it will allow
    > customization of isinstance to advertise that your class implements
    > MutableMapping, which is the right way to do it.


    Actually that's what I was originally thinking of doing but didn't go
    through with it in my first pass out of concern that users might want
    isinstance(bijection(), dict) to be True. Now that you bring it up, I
    agree that it's the correct way to do it, and have reimplemented
    bijection as a MutableMapping (ABCs are actually in Python 2.6). Take
    a peek at the new and improved http://bitbucket.org/jab/toys/src/tip/bijection.py
    if you get a chance and let me know how it looks!

    Anyone have any other feedback? For instance, is offering the __call__
    syntax for the inverse mapping wonderful or terrible, or maybe both?

    Thanks,
    Josh
     
    Joshua Bronson, Nov 20, 2009
    #5
  6. Joshua Bronson

    Terry Reedy Guest

    Joshua Bronson wrote:

    > Anyone have any other feedback? For instance, is offering the __call__
    > syntax for the inverse mapping wonderful or terrible, or maybe both?


    Terrible ;-)

    Use standard subscripting with slices, and only that, to both get and set.

    Let m[4] == m[4:] == 'abc' # m[4:] is suggested alternative addition
    Then m[:'abc'] == 4

    m[4:] passes slice(4,None,None) to __getitem__
    m[:'abc'] passes slice(None,'abc',None)

    It just happens that dict items and slices use the same notation, but
    they do, so take advantage of that. In fact, to emphasize the symmetry
    of the bijective map, consider disallowing m[key] as ambiguous and
    require m[key:], along with m[:key] to access and set.

    Note that m[slice(1,2,3):] passes slice(slice(1, 2, 3), None, None), so
    this approach does not even prevent using slices as keys/values.

    In __setitem__, m[a:b] which passes slice(a,b,None) would have to be an
    error. In __getitem__, it could either be a error or return True/False
    depending on whether the pair is in the map. But this depends on whether
    __contains__ only tests keys or is modified to test pairs.

    Terry Jan Reedy
     
    Terry Reedy, Nov 20, 2009
    #6
  7. On Nov 20, 3:09 pm, Terry Reedy <> wrote:
    > Joshua Bronson wrote:
    > > Anyone have any other feedback? For instance, is offering the __call__
    > > syntax for the inverse mapping wonderful or terrible, or maybe both?

    >
    > Terrible ;-)
    >
    > Use standard subscripting with slices, and only that, to both get and set..
    >
    > Let m[4] == m[4:] == 'abc' # m[4:] is suggested alternative addition
    > Then m[:'abc'] == 4
    >
    > m[4:] passes slice(4,None,None) to __getitem__
    > m[:'abc'] passes slice(None,'abc',None)
    >
    > It just happens that dict items and slices use the same notation, but
    > they do, so take advantage of that. In fact, to emphasize the symmetry
    > of the bijective map, consider disallowing m[key] as ambiguous and
    > require m[key:], along with m[:key] to access and set.
    >
    > Note that m[slice(1,2,3):] passes slice(slice(1, 2, 3), None, None), so
    > this approach does not even prevent using slices as keys/values.
    >
    > In __setitem__, m[a:b] which passes slice(a,b,None) would have to be an
    > error. In __getitem__, it could either be a error or return True/False
    > depending on whether the pair is in the map. But this depends on whether
    > __contains__ only tests keys or is modified to test pairs.
    >
    > Terry Jan Reedy


    absolutely genius. implemented in the latest version:
    http://bitbucket.org/jab/toys/src/tip/bijection.py

    thank you for the terrific idea!
     
    Joshua Bronson, Nov 20, 2009
    #7
  8. On Nov 20, 3:09 pm, Terry Reedy <> wrote:
    > Joshua Bronson wrote:
    > > Anyone have any other feedback? For instance, is offering the __call__
    > > syntax for the inverse mapping wonderful or terrible, or maybe both?

    >
    > Terrible ;-)
    >
    > Use standard subscripting with slices, and only that, to both get and set..
    >
    > Let m[4] == m[4:] == 'abc' # m[4:] is suggested alternative addition
    > Then m[:'abc'] == 4
    >
    > m[4:] passes slice(4,None,None) to __getitem__
    > m[:'abc'] passes slice(None,'abc',None)
    >
    > It just happens that dict items and slices use the same notation, but
    > they do, so take advantage of that. In fact, to emphasize the symmetry
    > of the bijective map, consider disallowing m[key] as ambiguous and
    > require m[key:], along with m[:key] to access and set.
    >
    > Note that m[slice(1,2,3):] passes slice(slice(1, 2, 3), None, None), so
    > this approach does not even prevent using slices as keys/values.
    >
    > In __setitem__, m[a:b] which passes slice(a,b,None) would have to be an
    > error. In __getitem__, it could either be a error or return True/False
    > depending on whether the pair is in the map. But this depends on whether
    > __contains__ only tests keys or is modified to test pairs.
    >
    > Terry Jan Reedy


    absolutely genius. implemented in the latest version:
    http://bitbucket.org/jab/toys/src/tip/bijection.py

    thank you for the terrific idea!
     
    Joshua Bronson, Nov 20, 2009
    #8
  9. On Nov 20, 3:09 pm, Terry Reedy <> wrote:
    > Use standard subscripting with slices, and only that, to both get and set..


    i did this for __delitem__ too, so you can do e.g. del m[:'abc'].

    > In fact, to emphasize the symmetry of the bijective map, consider
    > disallowing m[key] as ambiguous and require m[key:]


    my initial feeling is that i'd prefer to be able to say m[key], the
    defense being that it's not ambiguous if it's documented, and users
    who don't like it don't have to use it, while users who do like it
    won't be alienated. but i am definitely still open to this.

    > Note that m[slice(1,2,3):] passes slice(slice(1, 2, 3), None, None), so
    > this approach does not even prevent using slices as keys/values.


    except slices aren't hashable.</nitpick> but props for even thinking
    of that!

    > In __setitem__, m[a:b] which passes slice(a,b,None) would have to be an
    > error. In __getitem__, it could either be a error or return True/False
    > depending on whether the pair is in the map.


    i went with raising an error for __getitem__(slice(a,b,None)).
    returning True/False for this usage based on whether a -> b is in the
    bijection is an interesting idea, but i feel like it complicates
    things for no good reason: if that's what you wanted to know you'd
    just ask whether m[a] == b.

    > But this depends on whether __contains__ only tests keys or is modified to test pairs.


    i have __contains__ only testing keys, and similarly [i for i in
    bijection] only gives you the keys, again on the theory that deviating
    too much from the dict api increases the learning (adoption) curve, so
    i think we should only do it if it buys us a tremendous usability win.

    thanks again for your insightful input, i'm pretty psyched about how
    this is coming along!

    any further feedback is always welcome.

    josh
     
    Joshua Bronson, Nov 21, 2009
    #9
  10. On Nov 20, 3:09 pm, Terry Reedy <> wrote:
    > Use standard subscripting with slices, and only that, to both get and set..


    i did this for __delitem__ too, so you can do e.g. del m[:'abc'].

    > In fact, to emphasize the symmetry of the bijective map, consider
    > disallowing m[key] as ambiguous and require m[key:]


    my initial feeling is that i'd prefer to be able to say m[key], the
    defense being that it's not ambiguous if it's documented, and users
    who don't like it don't have to use it, while users who do like it
    won't be alienated. but i am definitely still open to this.

    > Note that m[slice(1,2,3):] passes slice(slice(1, 2, 3), None, None), so
    > this approach does not even prevent using slices as keys/values.


    except slices aren't hashable.</nitpick> but props for even thinking
    of that!

    > In __setitem__, m[a:b] which passes slice(a,b,None) would have to be an
    > error. In __getitem__, it could either be a error or return True/False
    > depending on whether the pair is in the map.


    i went with raising an error for __getitem__(slice(a,b,None)).
    returning True/False for this usage based on whether a -> b is in the
    bijection is an interesting idea, but i feel like it complicates
    things for no good reason: if that's what you wanted to know you'd
    just ask whether m[a] == b.

    > But this depends on whether __contains__ only tests keys or is modified to test pairs.


    i have __contains__ only testing keys, and similarly [i for i in
    bijection] only gives you the keys, again on the theory that deviating
    too much from the dict api increases the learning (adoption) curve, so
    i think we should only do it if it buys us a tremendous usability win.

    thanks again for your insightful input, i'm pretty psyched about how
    this is coming along!

    any further feedback is always welcome.

    josh
     
    Joshua Bronson, Nov 21, 2009
    #10
  11. On Nov 19, 3:24 pm, Joshua Bronson <> wrote:
    > I couldn't find a library providing a bijective map data structure
    > (allowing for constant-time lookups by value) in the few minutes I
    > looked, so I took a few more minutes to code one up:http://bitbucket.org/jab/toys/src/tip/bijection.py
    >
    > Is this at all worth releasing? Comments and suggestions welcome.
    >
    > Josh


    Hello Joshua,

    I have a few design ideas and comments for you.

    * The idea of using __call__ for looking-up inverse values was
    inspired. That is useable, clean, and easy to remember; however, as
    discussed below, there are issues though with its actual use in real
    code.

    * Am not excited by the inverse iterators. With just a regular
    mapping you can write:

    for a, b in m.items() ... # consider either a or b be the
    key and the other to be the value

    That meets all of the needs that would have been served by
    iter_inverse_keys() or iter_inverse_values() or whatnot. The mirrored
    API doesn't really provide much in the way of value added.

    * After exercising the API on a couple of samples, I'm worried that
    almost any inverse-method based API makes it difficult to review code
    while keeping straight the intended meaning of the forward and inverse
    relationships. Am thinking that it is simpler, faster, and clearer to
    just use two dictionaries -- that approach lets the variable names
    communicate the important info. For example, the following code helps
    keep the coder and code reviewer from conflating the forward and
    inverse directions:

    myurl = ip2url[myip]
    myip = url2ip[myurl]

    Contrast that with:

    myurl = site_bijection[myip]
    myip = site_bijection(myurl)

    With the latter, it is darned difficult to detect accidental
    conflation of brackets with parentheses.

    * So, I'm thinking that code needing a bijection would be better-off
    with two ordinary dicts, perhaps augmented by a couple of convenience
    functions:

    biject_add(site_bijection, ip=myip, url=myurl) # Create a
    new pairing, raise ValueError if either key
    # maps to
    more than one value (in violation of the
    # bijection
    invariant: one-to-one and onto)

    biject_remove(ip=myip) # Clear an
    entry from both dicts
    or
    biject_remove(url=myurl)

    Alternatively, another possible approach is to used use the class
    generation approach (such as that used by named_tuple()) to generate a
    custom bijection class with either attribute based or keyworded
    accessors:

    Attribute based accessors:

    site = Bijection('ip', 'url')
    site.url[myip] = myurl

    for ip, url in site.items() ...
    print site.ip[myurl]
    myurl = site.url.pop(myip)

    Keyword accessors:

    site = Bijection('ip', 'url')
    site.set(ip=myip, url=myurl)
    myurl = site.get(ip=myip)
    myip = set.get(url=myurl)
    myurl = site.pop(ip=myip)
    site.del(ip=myip)
    site.del(url=myurl)


    Hope these ideas help. The ultimate success of the Bijection code
    will depend on its clarity, simplicity, and speed. Experiment with
    various approaches to find-out which looks the best in real code. It
    cannot be error-prone or it is doomed. Also, it should not introduce
    much overhead processing or else people will avoid it. The API should
    be trivially simple so that people remember how to use it months after
    seeing it for the first time.

    Good luck and happy hunting,



    Raymond
     
    Raymond Hettinger, Nov 22, 2009
    #11
  12. Hey Raymond,

    Thanks for your thoughtful reply! I think your idea for a class-
    generation approach in the spirit of namedtuple is brilliant; looking
    forward to coding this up and seeing how it feels to use it.

    (By the way, it occurred to me that "bijection" is perhaps the wrong
    term to use for this data structure; really it's just an injective
    mapping, as it has no idea whether the function whose mappings it
    contains is also surjective. (Unless we take the domain, codomain, and
    range of the function being modeled to be exactly defined by the state
    of the mapping at any given time. But it feels more correct to me to
    interpret the mapping as a sampling of some underlying function, where
    the sampling can change but the function stays the same.) So I'm
    thinking of renaming the class injectivedict or idict instead of
    bijection. Is that crazy?)

    More responses inline:

    On Nov 21, 9:22 pm, Raymond Hettinger <> wrote:
    > * The idea of using __call__ for looking-up inverse values was
    > inspired.  That is useable, clean, and easy to remember; however, as
    > discussed below, there are issues though with its actual use in real
    > code.


    Totally agree the call syntax has issues. Did you happen to see
    Terry's suggestion to use slice syntax instead? Now *that* was
    inspired. It's also much better because it works for setitem and
    delitem too. I replaced the call syntax with the slice syntax on
    Friday night -- would be interested to hear whether you think it's an
    improvement.


    > * Am not excited by the inverse iterators.  With just a regular
    > mapping you can write:
    >
    >         for a, b in m.items() ...   # consider either a or b be the
    > key and the other to be the value
    >
    >   That meets all of the needs that would have been served by
    > iter_inverse_keys() or iter_inverse_values() or whatnot.  The mirrored
    > API doesn't really provide much in the way of value added.


    Hm, the one value I see the latest version of ``inverted`` adding (may
    not have been in the version you saw) is that you can pass it either a
    mapping, an iterable, or any object implementing an __inverted__
    method. So in one case it's just syntax sugar for writing [(v, k) for
    (k, v) in d.items()], but in other cases it's providing some
    abstraction.

    <snip much good feedback and ideas />

    > Hope these ideas help.  The ultimate success of the Bijection code
    > will depend on its clarity, simplicity, and speed.  Experiment with
    > various approaches to find-out which looks the best in real code.  It
    > cannot be error-prone or it is doomed.  Also, it should not introduce
    > much overhead processing or else people will avoid it.  The API should
    > be trivially simple so that people remember how to use it months after
    > seeing it for the first time.


    Thank you for the sage advice.

    Best,
    Josh
     
    Joshua Bronson, Nov 24, 2009
    #12
  13. Joshua Bronson wrote:
    > So I'm
    > thinking of renaming the class injectivedict or idict instead of
    > bijection. Is that crazy?)


    I think you'd be better off calling it something more
    down-to-earth such as bidict (bidirectional dictionary).
    That way people without an advanced degree in mathematics
    have a better shot at not being baffled by it.:)

    --
    Greg
     
    Gregory Ewing, Nov 24, 2009
    #13
  14. On Nov 24, 6:49 pm, Gregory Ewing <> wrote:
    > Joshua Bronson wrote:
    > > So I'm
    > > thinking of renaming the class injectivedict or idict instead of
    > > bijection. Is that crazy?)

    >
    > I think you'd be better off calling it something more
    > down-to-earth such as bidict (bidirectional dictionary).
    > That way people without an advanced degree in mathematics
    > have a better shot at not being baffled by it.:)
    >
    > --
    > Greg


    heh, duly noted:) bidict it is!
     
    Joshua Bronson, Nov 25, 2009
    #14
  15. Joshua Bronson, Nov 26, 2009
    #15
  16. Joshua Bronson

    Francis Carr Guest

    I was really inspired by this discussion thread! :)

    After much tinkering, I think I have a simpler solution. Just make
    the inverse mapping accessible via an attribute, -AND- bind the
    inverse of -THAT- mapping back to the original. The result is a
    python dict with NO NEW METHODS except this inverse-mapping
    attribute. I have posted it on code.activestate.com as <a
    href="http://code.activestate.com/recipes/576968/">Recipe 576968:
    Flipdict -- python dict that also maintains a one-to-one inverse
    mapping</a>

    -- F. Carr
     
    Francis Carr, Nov 27, 2009
    #16
  17. En Fri, 27 Nov 2009 15:12:36 -0300, Francis Carr <>
    escribió:

    > I was really inspired by this discussion thread! :)
    >
    > After much tinkering, I think I have a simpler solution. Just make
    > the inverse mapping accessible via an attribute, -AND- bind the
    > inverse of -THAT- mapping back to the original. The result is a
    > python dict with NO NEW METHODS except this inverse-mapping
    > attribute. I have posted it on code.activestate.com as <a
    > href="http://code.activestate.com/recipes/576968/">Recipe 576968:
    > Flipdict -- python dict that also maintains a one-to-one inverse
    > mapping</a>


    Nice idea! Just a couple of comments:

    Instead of:
    self._flip = dict.__new__(self.__class__)
    I'd write:
    self._flip = self.__class__()
    unless I'm missing something (but see the next point).

    Also, although Python's GC is able to handle them, I prefer to avoid
    circular references like those between x and x._flip. Making self._flip a
    weak reference (and dereferencing it in the property) should be enough.

    --
    Gabriel Genellina
     
    Gabriel Genellina, Nov 28, 2009
    #17
  18. On Nov 27, 9:36 pm, "Gabriel Genellina" <>
    wrote:
    > En Fri, 27 Nov 2009 15:12:36 -0300, Francis Carr <>  
    > escribió:
    >
    > > I was really inspired by this discussion thread! :)

    >
    > > After much tinkering, I think I have a simpler solution.  Just make
    > > the inverse mapping accessible via an attribute, -AND- bind the
    > > inverse of -THAT- mapping back to the original.  The result is a
    > > python dict with NO NEW METHODS except this inverse-mapping
    > > attribute.  I have posted it on code.activestate.com as <a
    > > href="http://code.activestate.com/recipes/576968/">Recipe 576968:
    > > Flipdict -- python dict that also maintains a one-to-one inverse
    > > mapping</a>

    >
    > Nice idea!


    Indeed! Thanks for sharing! I liked this so much I added something
    similar in http://bitbucket.org/jab/toys/src/tip/bidict.py (I made the
    inverse available via a .inv property, as well as via the unary ~
    operator (by analogy to bitwise inverse)). I also got rid of getinv,
    popinv, et al. to keep the API leaner as you recommend. I've kept the
    slice syntax though as well as namedbidect, so for now I guess I'm
    allowing for many ways to skin this cat.

    > Just a couple of comments:
    >
    > Instead of:
    >         self._flip = dict.__new__(self.__class__)
    > I'd write:
    >         self._flip = self.__class__()
    > unless I'm missing something (but see the next point).


    How would this not cause infinite recursion?

    > Also, although Python's GC is able to handle them, I prefer to avoid  
    > circular references like those between x and x._flip.  Making self._flip a  
    > weak reference (and dereferencing it in the property) should be enough.


    If both self._flip and self._flip._flip are weak references, no strong
    references to the inverse mapping survive leaving the constructor
    scope. Unless I'm missing something, only one of these can be a weak
    reference, and then you'd have to do something like this in the
    property to prevent "TypeError: FlipDict is not callable":

    @property
    def flip(self):
    try:
    # we're an inverse, self._flip is a weak reference
    return self._flip()
    except TypeError:
    # we're a forward mapping, self._flip is a strong
    reference
    return self._flip
     
    Joshua Bronson, Nov 28, 2009
    #18
  19. On Nov 19, 8:36 pm, Ben Finney <> wrote:
    > Carl Banks <> writes:
    > > On Nov 19, 3:24 pm, Joshua Bronson <> wrote:
    > > Apart from the GPL, it seems perfectly fine to release, and looks like
    > > an interesting strategy. I've wanted one of those once in a while,
    > > never enough to bother looking for one or writing one myself.

    >
    > I would think GPL is an excellent choice for such a library then, if the
    > author's intention is to encourage more software to be free software so
    > that it can incorporate a unique library like this.


    Well, yes and no.

    This bidict class sounds nice and full-featured, especially after the
    changes prompted by the fruitful discussion. I personally use inverse
    mappings on a regular basis, but for the most part, my data doesn't
    change all that dynamically (or performance doesn't really matter), so
    when I need to go backwards I often do something like:

    inverse_mapping = dict((y, x) for (x, y) in forward_mapping.iteritems
    ())

    Having said that, if I ever actually *need* something more full-
    featured to add to non-GPLed software, I'd just write it (and release
    it under a permissive license). IMHO, GPLing something this simple is
    really the tail trying to wag the dog.

    Case in point: I was just using rst2pdf to combine some restructured
    text and SVG images, using svglib. svglib had some bugs and didn't
    work right on my PDFs. svglib is not developed publicly, and the
    author is somewhat slow to accept patches. Since svglib is reasonably
    small, if it had been released under a permissive license, or even the
    LGPL, I probably would have just copied it into the rst2pdf repository
    and fixed it. If it were any smaller, I would have rewritten it. I
    don't own the rst2pdf package, and didn't really want a license
    discussion about 1K of source lines dictating a license change on 15K
    lines. As it is, I figure the svglib author will probably get around
    to fixing the bug at some point anyway, and for now I can easily use
    PDFs for my graphics input format, so I cleaned up and added to some
    old PDF code I had lying around, and released it as the open source
    pdfrw package, and now rst2pdf can use that to import PDFs as vector
    images without rasterizing them -- a new capability. So in this case,
    the GPL spurred open-source development, in exactly the same way that
    proprietary licenses do...

    I'm quite happy to *use* GPLed software (and greatly appreciate the
    authors' efforts), and I'm even sometimes willing to debug or add
    features and submit patches to GPLed software, and I might even have a
    (business, not political) reason to release some software under the
    GPL myself someday. But if I ever did release something under the GPL
    for business reasons, it would be because I also had the right to also
    offer a proprietary version. This would require that I owned _all_
    the code in the package, so the implication is: I'm not going to use
    your tiny little GPLed code in any major software I write for release,
    whether my software is GPLed or not.

    The LGPL is different. I view the LGPL as a statement of "if you ever
    add related functionality to this or fix a bug in this in a shipping
    product, I'd like to see the fix, please" and I could even see myself
    releasing something with this license under the right circumstances.

    Now if I were doing a web service, it would be a different story. I
    would be quite happy to add your GPLed software into the mix, so if
    that's a terrible thing, perhaps you should consider affero for your
    future offerings :)

    Best regards,
    Pat
     
    Patrick Maupin, Nov 28, 2009
    #19
  20. On Nov 27, 1:12 pm, Francis Carr <> wrote:
    > I was really inspired by this discussion thread! :)
    >
    > After much tinkering, I think I have a simpler solution.  Just make
    > the inverse mapping accessible via an attribute, -AND- bind the
    > inverse of -THAT- mapping back to the original.  The result is a
    > python dict with NO NEW METHODS except this inverse-mapping
    > attribute.  I have posted it on code.activestate.com as <a
    > href="http://code.activestate.com/recipes/576968/">Recipe 576968:
    > Flipdict -- python dict that also maintains a one-to-one inverse
    > mapping</a>
    >
    >  -- F. Carr


    I noticed the phonebook example in your ActiveState recipe and thought
    you might consider changing it to something like husbands to wives,
    since the names-to-phone-numbers relation is many-to-many. The built-
    in htmlentifydefs module provides fodder for a real-world example: It
    maintains name2codepoint and codepoint2name as two separate dicts.

    Raymond, do you think there might be any future in including a built-
    in bidict data structure in Python? At least there's one built-in
    module that might benefit from it.

    P.S. I moved bidict to its own repo at http://bitbucket.org/jab/bidict/
    and released it to PyPI as http://pypi.python.org/pypi/bidict.
     
    Joshua Bronson, Dec 1, 2009
    #20
    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. Replies:
    0
    Views:
    751
  2. Paul Moore
    Replies:
    0
    Views:
    619
    Paul Moore
    Mar 1, 2008
  3. Martin v. Löwis
    Replies:
    0
    Views:
    661
    Martin v. Löwis
    Mar 1, 2008
  4. Senthil Kumaran
    Replies:
    0
    Views:
    580
    Senthil Kumaran
    Jan 17, 2011
  5. R. David Murray
    Replies:
    0
    Views:
    762
    R. David Murray
    Jan 17, 2011
Loading...

Share This Page