patternpythonMinor
List querying capabilities in Python 3.x
Viewed 0 times
listcapabilitiespythonquerying
Problem
Either out of boredom, or my odd fascination with LINQ and SQL, this, thing, was created. Essentially, it provides list querying capabilities. At the moment, you can only query lists based on conditions, and sort them. Here's the source.
```
import operator as oper
class QueryableListCondition:
"""
A class used for conditions in
QueryableList.where(*conditions).
"""
def __init__(self, _operator: str, _value):
self._operator = _operator.lower()
self._value = _value
self._operators = {
"eq": oper.eq,
"ne": oper.ne,
"lt": oper.lt,
"gt": oper.gt,
"le": oper.le,
"ge": oper.ge
}
def compare(self, value):
"""
Compares the values, with value
as the first compared value. Compari-
sons look something like this:
value operator self.value
"""
return self._operatorsself._operator
def reverse_compare(self, value):
"""
Compares the values with self.value
as the second compared value. Compari-
sons look something like this:
value operator self.value
"""
return self._operatorsself._operator
class QueryableList:
"""
A special class which is like a
default Python list, except it
has querying capabilities.
"""
def __init__(self, _items: list):
self._items = _items
def select(self):
"""
'Select' self._items and return it as
a list. This is used in QueryableList.
where as the first argument.
"""
return self._items
def where(self, query_list: list, compare_type: str, *conditions: QueryableListCondition):
"""
Select elements from a QueryableList
based on conditions.
"""
result_list = []
for condition in conditions:
for item in se
py_listq.py```
import operator as oper
class QueryableListCondition:
"""
A class used for conditions in
QueryableList.where(*conditions).
"""
def __init__(self, _operator: str, _value):
self._operator = _operator.lower()
self._value = _value
self._operators = {
"eq": oper.eq,
"ne": oper.ne,
"lt": oper.lt,
"gt": oper.gt,
"le": oper.le,
"ge": oper.ge
}
def compare(self, value):
"""
Compares the values, with value
as the first compared value. Compari-
sons look something like this:
value operator self.value
"""
return self._operatorsself._operator
def reverse_compare(self, value):
"""
Compares the values with self.value
as the second compared value. Compari-
sons look something like this:
value operator self.value
"""
return self._operatorsself._operator
class QueryableList:
"""
A special class which is like a
default Python list, except it
has querying capabilities.
"""
def __init__(self, _items: list):
self._items = _items
def select(self):
"""
'Select' self._items and return it as
a list. This is used in QueryableList.
where as the first argument.
"""
return self._items
def where(self, query_list: list, compare_type: str, *conditions: QueryableListCondition):
"""
Select elements from a QueryableList
based on conditions.
"""
result_list = []
for condition in conditions:
for item in se
Solution
The
The case of an unsupported operator is not handled. The class happily keys the user create a condition with an unsupported operator, and the program will crash later. Always validate the received function parameters, and signal problems as early as possible.
After the condition is created, there's no need to hold on to the string representation of the operator only to lookup the real operator in the common mapping table. Better to lookup once, in the constructor, and store the real operator, not its string representation.
If a condition has two modes, non-reverse and reverse, consider using a boolean to represent that. Forcing the user to remember to pass on the right magic string is not very user friendly and prone to errors. If you use a boolean, there are 2 possible values, instead of an infinite spacer of invalid values. More intuitive api, and higher chance that users get it right.
These conditions cannot both be true at the same time:
So the second one should be an
I think par of the reason the current implementation feels clunky is that it is not fluent. For example, a fluent api to select a list of name-email pairs from a collection objects, matching a name, ordered by age, look something like this:
disclaimer: inspired by queries in Django
_operators attribute is not specific to an instance but a common mapping table that can be shared by all instances, so it could be turned into a class attribute instead.The case of an unsupported operator is not handled. The class happily keys the user create a condition with an unsupported operator, and the program will crash later. Always validate the received function parameters, and signal problems as early as possible.
After the condition is created, there's no need to hold on to the string representation of the operator only to lookup the real operator in the common mapping table. Better to lookup once, in the constructor, and store the real operator, not its string representation.
If a condition has two modes, non-reverse and reverse, consider using a boolean to represent that. Forcing the user to remember to pass on the right magic string is not very user friendly and prone to errors. If you use a boolean, there are 2 possible values, instead of an infinite spacer of invalid values. More intuitive api, and higher chance that users get it right.
These conditions cannot both be true at the same time:
if compare_type.lower() == "non-reverse":
# ...
if compare_type.lower() == "reverse":So the second one should be an
elif. Converting the compare type variable to lower case twice is also not pretty.I think par of the reason the current implementation feels clunky is that it is not fluent. For example, a fluent api to select a list of name-email pairs from a collection objects, matching a name, ordered by age, look something like this:
queryset.where(name="Jack").order_by("age").select("name", "email")disclaimer: inspired by queries in Django
Code Snippets
if compare_type.lower() == "non-reverse":
# ...
if compare_type.lower() == "reverse":queryset.where(name="Jack").order_by("age").select("name", "email")Context
StackExchange Code Review Q#94853, answer score: 6
Revisions (0)
No revisions yet.