On Apr 22, 11:34 pm, Aaron Brady <castiro...@gmail.com> wrote: > On Apr 22, 11:52 am, Aaron Brady <castiro...@gmail.com> wrote: > > > On Apr 22, 12:09 am, Chris Rebert <c...@rebertia.com> wrote: > > > > On Tue, Apr 21, 2009 at 5:51 PM, Aaron Brady <castiro...@gmail.com> wrote: > > > > Hi all, > > > > > I think Python should have a relation class in the standard library. > > > > Fat chance. > > > > Perhaps I'm not understanding "relation" correctly, but are you not > > > aware ofhttp://docs.python.org/library/sqlite3.html? > > > > Cheers, > > > Chris > > > -- > > > I have a blog:http://blog.rebertia.com > snip > > It only supports numbers and strings. > > snip > > My point is that in undirected relations, it's redundant to maintain > reciprocal membership. snip > Here is another sample: > > Tree= Relation( ( "parent", "child", "direction" ) ) > nodes= [ object( ) for _ in range( 10 ) ] > Tree( ( nodes[ 0 ], nodes[ 1 ], "left" ) ) > Tree( ( nodes[ 0 ], nodes[ 2 ], "right" ) ) snip > 'select' would need the context > of its caller, which isn't available. snip
Or, pass 'locals()'. Actually, I discovered an interesting catch to 'locals()'. When you don't use a variable that's in an outer scope in a function, it doesn't appear in 'locals()'. However, if you just use it in a blank statement, it magically appears. >>> def f( ): ... x= [] ... def g( ): ... print( locals( ) ) ... g( ) ... >>> f( ) {} >>> def f( ): ... x= [] ... def g( ): ... x # empty use of 'x' ... print( locals( ) ) ... g( ) ... >>> f( ) # changes the contents of 'locals()' {'x': []} Here is what the docs have to say about *that*: "Free variables are returned by locals() when it is called in a function block." Since 'x' doesn't appear in the function, 'g' in this case, it isn't included in 'g's free variables. 'eval' actually therefore doesn't return exactly the result of evaluating a string where it's used. >>> def f( ): ... x= [] ... def g( ): ... print( eval( 'x' ) ) ... g( ) ... >>> f( ) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 5, in f File "<stdin>", line 4, in g File "<string>", line 1, in <module> NameError: name 'x' is not defined The 'eval' function docs do assert otherwise though: "...the expression is executed in the environment where eval() is called." 'eval' doesn't obviously keep its contract as shown. > I think some kind of markers would have to replace any identifiers it > would use: > > recordset= Tree.select( [ "child" ], > "parent is %var and direction=='left'", nodes[0] ) snip The 'sqlite3' module just uses a question mark. Despite the fact that variables are separated from exactly where they are used, it has the advantage, as S. D'Aprano pointed out nearby, of being able to use queries independently of them, as well as of being legal Python. If anyone is still reading, there is a further nifty-cool construct that the 'Relation' class can offer. Given the earlier definition of 'Tree' as an instance of 'Relation', a new class can combine property descriptors with the select statement. class TreeNode: relation= Tree left= Relation.getter( "child", "parent is ? and direction=='left'" ) nodes= [ TreeNode( ) for _ in range( 10 ) ] record= nodes[ 0 ].left Getting the 'left' member of an instance of this class is just syntactic sugar for a long-hand query. It only returns one field of an arbitrary element of the recordset. It only allows one '?' in the definition, since getter functions only take one argument: self! Here's the definition of 'getter': @staticmethod def getter( field, where ): def _getter( self ): return getattr( next( self.relation.select( '*', where, self ) ), field ) return property( fget= _getter ) It could be an attribute of the module, not specifically of the Relation class... which might even be advisable. You could even combine the 'relation' member into the call to 'getter', so 'getter' wouldn't have to retrieve it as 'self.relation'. That has the disadvantage of increasing repetition of the relation in the class statement, but enables a class to participate in multiple relations. Come to think of it, you could write it as 'Tree.getter', and compromise. Then 'getter' becomes: def getter( self, field, where ): def _getter( ob ): return getattr( next( self.select( '*', where, ob ) ), field ) return property( fget= _getter ) and 'TreeNode' becomes: class TreeNode: left= Tree.getter( "child", "parent is ? and direction=='left'" ) You might want 'getter', 'getiter', and 'getone' methods, which return a set of, an iterator over, and just the field of an arbitrary one of the matching records respectively. Proof-of-concept isn't even complete; but nonetheless, pretty cool, eh? -- http://mail.python.org/mailman/listinfo/python-list