So, how much of LINQ is already doable in Python? Well, LINQ works on the IEnumerable
To implement the methods used on IEnumberable-implementing classes and used by the new syntax added by LINQ (covered later), C# 3 introduced extension methods. You can think of these as methods you monkeypatch on to classes in Python. By importing an extension method into a namespace it is automatically injected into the class in the current namespace. Python does not quite have that, although if you imported a metaclass as __metaclass__ at the global level of a module it could be made to do the same thing. So a point to LINQ and the simpler automation of injecion thanks to typing.
But then again, this kind of injection is not needed. If you look at the syntax added by LINQ, an example form being
there is nothing there cannot be done with generator expressions and sorted() and thus not require adding support to a class to work with some special syntax. Take the example
from itemName in srcExpr where predExpr orderby keyExpr select selExpr
What is this trying to do? Well, it is taking each item that is exactly 5 characters long, sorting it based on itself, and putting it to uppercase. Nothing complex.
from s in names
where s.Length == 5
orderby s
select s.ToUpper()
But how would you do that in Python?
All we are doing is passing a generator expression that is filtering on the length of 5 on the items from the iterator for 'names', calling upper() on each, and then sorting that result. Now that is not lazy because of the sorted call, but just stick it in a lambda call if you need that sort of laziness (leaving off the sorted call would make it entirely lazy thanks to the genexp). So, in general, no power is lost between the Python and C# version.
sorted(s.upper() for s in names if len(s) == 5)
But maybe I am just lucky with the first example from LINQ. Let's look at the full syntax:
Let's take a quick look at the parts that are available. You can iterate over return iterables and filter on each returned item from the iterable (from and where). You can order them based on a lambda expression (orderby), and then modify based on another lambda expression (select) and group things together based on a filter lambda expression and on a key extraction lambda function (group).
from itemName in srcExpr
((from itemName in srcExpr) | (where predExpr))*
(orderby (keyExpr (ascending|descending)?)+)?
((select selExpr | (group selExpr by keyExpr))
There is nothing there that you can't do in Python with a generator expression and a call to sorted(). You can implement from, where, and select in a generator expression. sorted() takes care of orderby. And for group, you can use itertools.groupby(). So, using the same variable names in the example, here is roughly how to reformat in Python:
for the genexp, and then pass that to
selExpr for itemName in srcExpr((if predExpr)? (for itemName in srcExpr)?)*
And you can pass the genexp through itertools.groupby() before passing on to sorted() (see this recipe, by yours truly and in the dead-tree version of the Python Cookbook, on how to use itertools.groupby()).
sorted(genexp, key=keyExpr, reverse=(True if descending else False))
Basically the syntax support for LINQ is doable syntactically in Python already using a much more general system. The biggest difference is the use of sorted() with a genexp and itertools.groupby() instead of being a fully enclosed solution in syntax. That and the nicer way of doing monkeypatching on classes with extension methods are maybe the only perks to how LINQ does it. Otherwise their is nothing there Python can't already do. And I am not sure if you can use the query syntax anywhere like you can use a genexp anywhere in Python.
Go, Python, go! At least C# is becoming more tolerable. =) Too bad I can't say the same of Java.
7 comments:
Eh, hrm. I was under the imnpression that LINQ was more than just sugar: doesn't it also provide a common query syntax for disparate stores (DB's, XML, collections, etc)? See http://www.devx.com/dbzone/Article/30414. That seems to me to be the "more important" benefit.
Yeah, it's translating that syntax into a query run outside of the process that seems like the interesting part.
I think SQLComp starts going in that direction, though: http://subway.python-hosting.com/wiki/SQLComp
I suspect that could be made to work on generator expressions instead of the lambda+list comprehension stuff it is doing now. That would make it look very much like LINQ.
LINQ is more than you seem to understand. Querying upon any implementor of IEnumerable is merely the lowest common denominator of LINQ, as there are other interfaces it can work with. If LINQ worked souly with sources as IEnumerable objects, it would be horrible for use with any database, which is one of the prime purposes of LINQ, because it would need to iterate over every record in a table to match them, in the application level, but this is not what LINQ does. Instead, in the case of a database, or other known source it can work well with, it can convert the Lambda Expression (akin to Python's AST objects) into an actual SQL query and send this to the database for execution and processing, returning the results back that you need. IEnumerable is only the fallback LINQ can work with in the absense of anything better, so the solutions you (and many others) talk of as evidence that "Python doesn't need LINQ" is pretty false. I would love to see an AST-based equivalent to LINQ in Python. Of course, we already have similar patterns being used, just look at Divmod's Axiom, or the query syntax of SQLObject. This is a start, and with AST could match LINQ in power.
Also--though this is comparatively a nitpick--itertools.groupBy is not the same thing as C# GroupBy(), which is more useful.
Does link default to caseless sorting? If not, then the python version is slightly different -- though probably a bugfix.
You seem to have completely misunderstood LINQ. Read more about it!
This is also an extremely old post. I have since attended demos by the lead of the C# team and realize how I misunderstood things.
Post a Comment