Reputation: 2866
I am experimenting with fluent interfaces in Python.
An example of a fluent sql query generator would look something like this in usage:
sql.select('foo').select('bar').from('sometable').tostring()
I quickly realized that the ability to define nested classes recursively would probably help.
class sql:
class select:
class select # <-- HERE
def __init__(self, dbcolumn, astype=None, asname=None):
self.dbcolumn = dbcolumn
self.astype = astype
self.asname = asname
In the line marked with the comment '# <-- HERE':
I want this nested class reference to refer to the same 'select' class definition of the containing class.
Is this possible somehow? Maybe using some keyword that I am not aware of?
Upvotes: 4
Views: 6266
Reputation: 2866
As the title of the question indicated, I really wanted to know how to recursively nest a class definition. I am sure there are many ways to 'skin the fluent-interface-in-python cat'.
I found a way to recursively nest a class definition using decorators:
# class decorator
def recursiveclass(decorated_class):
decorated_class.__dict__[decorated_class.__name__] = decorated_class
return decorated_class
#fluent interface
class sql:
@recursiveclass
class select:
def __init__(self, dbcolumn, astype=None, asname=None):
self.dbcolumn = dbcolumn
self.astype = astype
self.asname = asname
Please note that I am not saying this is the best way to implement a fluent interface in Python. I am merely experimenting.
To verify:
dir(sql.select.select.select.select)
['__doc__', '__init__', '__module__', 'select']
Upvotes: 0
Reputation: 1124238
You are confusing classes with methods.
select
is a method on an instance of class sql
, not a nested class. It could return another instance.
class SelectStatement(object):
def select(self, tablename):
return SelectStatement()
class SQL(object):
def select(self, tablename):
return SelectStatement()
Take a look at the source code of SQLAlchemy; it does exactly that; generate SQL from a structure python classes, where you can refine a query by calling methods on instances in just such a manner. A series of joins for example, can be chained:
q = session.query(User).join(User.orders).join(Order.items).join(Item.keywords)
Upvotes: 0
Reputation: 318688
There is no need for "recursive class definitions". All you need to do to allow chaining is to return self
in your methods (or a instance of the same class if your objects are not mutable or some methods shouldn't modify the input object).
Example:
>>> class SQL(object):
... def __init__(self):
... self.columns = []
... def select(self, col):
... self.columns.append(col)
... return self
...
>>> s = SQL()
>>> s.select('foo').select('bar').columns
['foo', 'bar']
Upvotes: 7