Dictionary type access to list of instances 
Author Message
 Dictionary type access to list of instances

Hi

If I have a list of instances, each with a property which I want to use as a
key to access the list in dictionary-like manner, what would be the best way
of doing this?

eg.
func get(list, property, value):
    for x in list:
        if getattr(x, property) == value:
            return x

OR

func get(list, property, value):
    return filter(lambda x: getattr(x, property) == value, list)[0]

Note: Normally property would be fixed, and I probably don't need it as
flexible as this. i.e. get(list, value) would probably satisfy my needs.

I could do it using functions, or even overriding the UserDict class, but
ultimately I'll use a similar algorithm. Any comments or ideas appreciated.

Thanks
Grant Beasley



Fri, 13 Aug 2004 20:59:28 GMT  
 Dictionary type access to list of instances


Quote:
> If I have a list of instances, each with a property which I want to
> use as a key to access the list in dictionary-like manner, what would
> be the best way of doing this?

Is there any reason why you can't just store your instances in a dictionary
using the property as the key?

--

int month(char *p){return(124864/((p[0]+p[1]-p[2]&0x1f)+1)%12)["\5\x8\3"
"\6\7\xb\1\x9\xa\2\0\4"];} // Who said my code was obscure?



Fri, 13 Aug 2004 21:20:32 GMT  
 Dictionary type access to list of instances

Quote:

> Hi

> If I have a list of instances, each with a property which I want to use as a
> key to access the list in dictionary-like manner, what would be the best way
> of doing this?

> eg.
> func get(list, property, value):
>     for x in list:
>         if getattr(x, property) == value:
>             return x

In this usage, getattr will raise an Attribute error if property doesn't
exist which might not be what you want.

Here is an attempt using list comprehensions and the built-in list.index
function.  I have created a NotEqual class so that getattr can return a
default value is never equal to any other instance.  This is because
None might be a valid value for a property.

class NotEqual:
     """instances of this class are never equal to anything"""
     def __eq__(self, other):
         return 0

def get(inputList, property, value, default=NotEqual()):
     try:
         properties = [getattr(x, property, default) for x in inputList]
         return inputList[properties.index(value)]
     except ValueError:
         raise ValueError, "attribute %s with value %s not found"%(
             `property`, `value`)

if __name__ == "__main__":
     class Foo:
         def __init__(self):
             self.dog = 1

     foo = Foo()
     print get([foo], 'dog', 1)
     try:
         get([foo], 'dog', 2)
     except ValueError, msg:
         print 'error caught:', msg

     try:
         get([foo], 'cat', 'raisin')
     except ValueError, msg:
         print 'error caught:', msg



Fri, 13 Aug 2004 23:40:04 GMT  
 Dictionary type access to list of instances

Quote:

> In this usage, getattr will raise an Attribute error if property doesn't
> exist which might not be what you want.

> Here is an attempt using list comprehensions and the built-in list.index
> function.  I have created a NotEqual class so that getattr can return a
> default value is never equal to any other instance.  This is because
> None might be a valid value for a property.

> class NotEqual: [...]

> def get(inputList, property, value, default=NotEqual()):
>      try:
>          properties = [getattr(x, property, default) for x in inputList]
>          return inputList[properties.index(value)]
>      [...]

Here are some alternatives.  (Each must be wrapped in a for loop.)

  (1)   if hasattr(x, property) and getattr(x, property) == value:
             return x

  (2)   try:
            if getattr(x, property) == value:
                return x
        except AttributeError:
            pass  # x doesn't have that property; skip it

  (3)   if getattr(x, property, not value) == value:
             return x

I'm fond of the last one.  No builtin value will have
"value == not value", and user-defined types will typically
avoid it too.

## Jason Orendorff    http://www.jorendorff.com/



Sat, 14 Aug 2004 08:52:13 GMT  
 Dictionary type access to list of instances
Thanks for the response

Firstly, I really like your NotEqual class, it solves a bunch of other
problems I'm experiencing, because as you point out, None can sometimes be a
valid value.

The only part I don't like about your solution is the fact that the
properties array will be generated for all instances, whereas, using a for
loop that returns the instance when it finds it, not all instances need to
be inspected for the property. So this might result in better efficiency (In
the best case where the instance required is the first in the list).

Obviously catching the attribute exception is a better solution though, and
I'll probably integrate that into my solution. Currently I'm using my filter
solution just because I tend to prefer things I can do on one line instead
of having to write a complete new subroutine (which is why I enjoy python so
much), but I'll probably adapt that soon.

What I'm ultimately aiming for is a database-like view of a list of
instances, so that I can use SQL commands to get instances. So in the
analogy, a list of instances would be a table, with each instance a record,
and each property a field.

eg.
class User:
    def __init__(self, username, accesslevel):
        self.username = username
        self.accesslevel = accesslevel

list = [User('Bob', 1), User('Jim', 2), User('Pete', 2)]

Then using my imaginary function ListSQL:

ListSQL("select * from list where accesslevel = 2")
would return a list consisting of the Jim and Pete instances.

or ListSQL("select username, accesslevel from list where accesslevel = 2")
would return [('Jim', 2), ('Pete',2)] - i.e. a List of tuples.

Is this an interesting / useful idea? Has it been done before? Any comments?

Thanks
Grant


Quote:

> > Hi

> > If I have a list of instances, each with a property which I want to use
as a
> > key to access the list in dictionary-like manner, what would be the best
 way
> > of doing this?

> > eg.
> > func get(list, property, value):
> >     for x in list:
> >         if getattr(x, property) == value:
> >             return x

> In this usage, getattr will raise an Attribute error if property doesn't
> exist which might not be what you want.

> Here is an attempt using list comprehensions and the built-in list.index
> function.  I have created a NotEqual class so that getattr can return a
> default value is never equal to any other instance.  This is because
> None might be a valid value for a property.

> class NotEqual:
>      """instances of this class are never equal to anything"""
>      def __eq__(self, other):
>          return 0

> def get(inputList, property, value, default=NotEqual()):
>      try:
>          properties = [getattr(x, property, default) for x in inputList]
>          return inputList[properties.index(value)]
>      except ValueError:
>          raise ValueError, "attribute %s with value %s not found"%(
>              `property`, `value`)

> if __name__ == "__main__":
>      class Foo:
>          def __init__(self):
>              self.dog = 1

>      foo = Foo()
>      print get([foo], 'dog', 1)
>      try:
>          get([foo], 'dog', 2)
>      except ValueError, msg:
>          print 'error caught:', msg

>      try:
>          get([foo], 'cat', 'raisin')
>      except ValueError, msg:
>          print 'error caught:', msg



Sat, 14 Aug 2004 15:44:33 GMT  
 Dictionary type access to list of instances

Quote:

> What I'm ultimately aiming for is a database-like view of a list of
> instances, so that I can use SQL commands to get instances. So in the
> analogy, a list of instances would be a table, with each instance a record,
> and each property a field.

> eg.
> class User:
>     def __init__(self, username, accesslevel):
>         self.username = username
>         self.accesslevel = accesslevel

> list = [User('Bob', 1), User('Jim', 2), User('Pete', 2)]

> Then using my imaginary function ListSQL:

> ListSQL("select * from list where accesslevel = 2")
> would return a list consisting of the Jim and Pete instances.

> or ListSQL("select username, accesslevel from list where accesslevel = 2")
> would return [('Jim', 2), ('Pete',2)] - i.e. a List of tuples.

> Is this an interesting / useful idea? Has it been done before? Any comments?

You might want to check out database objects
http://www.xs4all.nl/~bsarempt/python/dbobj.html

or gadly, a python relational database
http://www.chordate.com/kwParsing/gadfly.html#table

I have had a great deal of success with metakit which I endorse heavily
http://www.equi4.com/metakit/python.html

and I willing to bet Zope has a solution in this realm as well...
http://www.zope.org/

These might help from not reinventing the wheel.

Brian Kelley



Sat, 14 Aug 2004 22:15:51 GMT  
 Dictionary type access to list of instances
[...]
Quote:

>What I'm ultimately aiming for is a database-like view of a list of
>instances, so that I can use SQL commands to get instances. So in the
>analogy, a list of instances would be a table, with each instance a record,
>and each property a field.

>eg.
>class User:
>    def __init__(self, username, accesslevel):
>        self.username = username
>        self.accesslevel = accesslevel

>list = [User('Bob', 1), User('Jim', 2), User('Pete', 2)]

>Then using my imaginary function ListSQL:

>ListSQL("select * from list where accesslevel = 2")
>would return a list consisting of the Jim and Pete instances.

>or ListSQL("select username, accesslevel from list where accesslevel = 2")
>would return [('Jim', 2), ('Pete',2)] - i.e. a List of tuples.

>Is this an interesting / useful idea? Has it been done before? Any comments?

Well, it got me to try out properties and weak references and
modifying a class variable before use of the class ;-).

I'm on thin ice here, so comments from someone who's been using
these features a while would be welcome. ;-)

Below is an interaction from my categorizer.py (see listing at end):
In this case I am just using name strings as objects, but they
could be any objects. The Categorizer class wraps objects you
give to it in a new object which also has a propery (2.2 style),
whose value you can also specify, or the object will belong to
the default category with prop==None. The class keeps track of all
instances in categories [lists] of equal prop values. Actually
it's a dictionary with the properties for keys, and the corresponding
values are lists of weak references to wrapper objects with that key
as property value.

If you update the property of a wrapper object, it's prop value category
membership is automatically updated, and likewise if a wrapper object
is deleted (using weak references seems to work in allowing the ref
count to go to zero and the destructor gets called) ;-).

The access methods to get lists of objects etc are accessible as
methods through any wrapper object instance.

The getCategorizerClass function delivers a class with a separate
{prop_value:[obj_weak_refs]} dictionary, to that several distinct
categories can operate separately. I used a list below (ulist) of
instances to bind the wrapper objects, but they can be bound any
way that keeps them alive until you want to delete them.

I'm not sure this is a practical solution to your problem, but it
was an interesting exercise. I'd be interested in a clean and proper
meta-class solution to generating the Category classes that
categorizer.getCategorizerClass() returns.

Regards,
Bengt Richter

______________________________________________________________

 Python 2.2 (#28, Dec 21 2001, 12:21:22) [MSC 32 bit (Intel)] on win32
 Type "help", "copyright", "credits" or "license" for more information.
 >>> import categorizer
 >>> NamePriv = categorizer.getCategorizerClass()
 >>> ulist = [NamePriv('Bob',1), NamePriv('Jim',2), NamePriv('Pete',2)]
 >>> ulist[0].get_objs(1)
 ['Bob']
 >>> ulist[0].get_objs(2)
 ['Jim', 'Pete']
 >>> ulist[1].prop = 1
 >>> ulist[0].get_objs(1)
 ['Bob', 'Jim']
 >>> ulist[0].get_objs(2)
 ['Pete']
 >>> del ulist[0]
 >>> ulist[0].get_objs(1)
 ['Jim']
 >>> ulist[0].get_objs(2)
 ['Pete']
 >>> ulist[0].prop
 1
 >>> ulist[0].obj
 'Jim'
 >>> ulist[0].prop =2
 >>> ulist[0].get_objs(1)
 []
 >>> ulist[0].get_objs(2)
 ['Pete', 'Jim']
 >>> ulist[0].get_obj(2)
 'Pete'

(Note that get_obj gets only the first available, unlike get_objs (plural)).
______________________________________________________________

# categorizer.py
# (strawman discussion example, not production code)
"""Use getCategorizerClass([dict]) to get a Categorizer class"""

import weakref

def getCategorizerClass(d=None):
    """getCategorizerClass([dict]) => Categorizer class"""

    class Categorizer(object):
        """class to wrap object in another with categorizing property"""
        _pd = d or {}  #fresh d instance dict as class variable of form:
                 # { prop_value: [list of weak references to wrapper instances having a given prop value] }
        def __init__(self, v=None, p=None):
            """init Categorizer box instance with obj and property"""
            self.__doc__ = """instance of class Categorizer having property prop"""
            self.obj = v  # the obj in the box
            self.wsr = weakref.ref(self)  # weak self ref to use in dict
            self.prop = p   # triggers set_prop to add to list of objects with p as property

        def __del__(self):
            try:
               self._pd[self._prop].remove(self.wsr) #remove self from current prop's list
               del self.wsr
            except:
                pass

        def get_cat_boxes(self,v=None):
            """find Categorizer box instance(s) whose property is a given value"""
            return filter(None, [y() for y in (self._pd.get(v) or [])])

        def get_objs(self,v=None):
            """get objs from Categorizer box instance(s) whose property is a given value"""
            return [x.obj for x in filter(None, [y() for y in (self._pd.get(v) or [])])]

        def get_obj(self,v=None):
            """get first available obj from Categorizer box instance(s) whose property is a given value"""
            for y in (self._pd.get(v) or []):
                yo = y()                # get obj from weak ref
                if yo: return yo.obj    # return first found only
            return None                 # none found

        def get_prop(self):
            """triggered by box_instance.prop in expression"""
            return self._prop
        def set_prop(self, p):
            """triggered by box_instance.prop on left hand side of '='"""
            try:
               self._pd[self._prop].remove(self.wsr) #remove self from previous prop's list
            except:
                pass
            self._prop = p
            try:
                self._pd[p].append(self.wsr)      # put self on list for prop==p
            except:
                self._pd[p] = [self.wsr]          # ditto

        # tie in property methods
        prop = property(get_prop, set_prop, None, #no deleting the prop
                  "Property 'prop' of this instance")

    return Categorizer
______________________________________________________________



Sun, 15 Aug 2004 16:59:04 GMT  
 
 [ 7 post ] 

 Relevant Pages 

1. 'Access on current instance of abstract type

2. type is access cf type is access all?

3. Accessing Dictionary values from within the Dictionary

4. type(type) is an object, not an instance

5. Speeding dictionary lookup of instances

6. Printing a dictionary of class instances

7. instances of classes and instances of instances

8. type mismatches/access type

9. Access types vs. Record types as procedure parameters

10. access mode to access type

11. Type resolution & classwide access types

12. Inheritance of abstract types: accessing the parent type (F2003 standard question)

 

 
Powered by phpBB® Forum Software