Howdy Folks,
currently I am pondering about how to implement an enhanced list, which provides an easy, intuitive and short notation for selecting subsets to be taken into account by specific list methods (it must be Python 2.7.18 compatible). The minimal code snippet belonging to the best idea I came up so far, does the following (where the method ‘elute’ just is an example):
print ( enhList1 )
print ( enhList1.elute( (Elem > 5) & (Elem < 11) ) ) #new notation
print ( enhList1 )
leads to ==>
[1, 3, 5, 7, 9, 11, 13, 15]
[7, 9]
[1, 3, 5, 11, 13, 15]
and
print ( enhList2 )
print ( enhList2.elute( (Elem.a > 5) & (Elem.a < 11) ) ) #new notation
print ( enhList2 )
leads to ==>
[a = 1, a = 3, a = 5, a = 7, a = 9, a = 11, a = 13, a = 15]
[a = 7, a = 9]
[a = 1, a = 3, a = 5, a = 11, a = 13, a = 15]
and
print ( enhList3 )
print ( enhList3.elute( (Elem[1] > 5) & (Elem[1] < 11) ) ) #new notation
print ( enhList3 )
leads to ==>
[[1, 1, 1], [3, 3, 3], [5, 5, 5], [7, 7, 7], [9, 9, 9], [11, 11, 11], [13, 13, 13], [15, 15, 15]]
[[7, 7, 7], [9, 9, 9]]
[[1, 1, 1], [3, 3, 3], [5, 5, 5], [11, 11, 11], [13, 13, 13], [15, 15, 15]]
and
print ( enhList4 )
print ( enhList4.elute( (Elem['a'] > 5) & (Elem['a'] < 11) ) ) #new notation
print ( enhList4 )
leads to ==>
[{'a': 1}, {'a': 3}, {'a': 5}, {'a': 7}, {'a': 9}, {'a': 11}, {'a': 13}, {'a': 15}]
[{'a': 7}, {'a': 9}]
[{'a': 1}, {'a': 3}, {'a': 5}, {'a': 11}, {'a': 13}, {'a': 15}]
So, my question now is: any ideas to do that better? Any ideas to improve that? Any ideas for a more elegant solution? And last but not least: any ideas how to change the code to also be Python 3.* compatible (yet it leads to: “TypeError: ‘>’ not supported between instances of ‘type’ and ‘int’”)?
The belonging code is the following:
# -*- coding: utf-8 -*-
#import common libraries
from threading import Lock
#helper class - element selection string type for enhanced list operations
class EnhList_SelStr(str):
"""
An instance of EnhList_SelStr is resp. can be given as the parameter
when calling the 'elute' method of an 'EnhList' instance.
It is a string type, which can be evaluated to determine,
whether an element of the enhanced list has to be taken into account
by a belonging list operation or not.
It can be extended using comparison operators and
the bitwise and/or operators as.
The current element to be taken into account (or not) is denoted by
'elem'; if the EnhList_SelStr e.g. is:
'elem > 5'
the current list element is taken into account, if the evaluation is true,
which in this example means: if the element is bigger than 5.
"""
#initialisation
def __init__(self, *params):
""" Ensure the standard str behaviour. """
str.__init__(self, *params)
#generate comparison methods __lt__ ... __ne__,
#called if "<" ... "!=" are used on self
for operatorT in ( ('__lt__', '<'), ('__le__', '<='), ('__eq__', '=='), \
('__gt__', '>'), ('__ge__', '>='), ('__ne__', '!=') ):
exec( """def {0}(self, ohs):
return EnhList_SelStr("(%s {1} %s)" % (self, ohs))
""".format(*operatorT))
#called if the 'bitwise and' operator '&' is used on self
def __and__(self, ohs):
""" As there is no hook for the 'logical and' operator,
the 'bitwise and' is 'abused' instead. """
return EnhList_SelStr("(%s and %s)" % (self, ohs))
#called if the 'bitwise or' operator '|' is used on self
def __or__(self, ohs):
""" As there is no hook for the 'logical or' operator,
the 'bitwise or' is 'abused' instead. """
return EnhList_SelStr("(%s or %s)" % (self, ohs))
#helper class - meta class (just) for the Elem class
class EnhList_ElemMeta(type):
"""
This metaclass (just) is used to hook into the get attribute resp.
get item mechanism of the CLASS Elem (not of it's instances!).
"""
#method called by "Elem.nameS"
@classmethod
def __getattribute__(cls, nameS):
""" """
return EnhList_SelStr("elem.%s" % nameS)
#method called by "Elem[keyO]"
@classmethod
def __getitem__(cls, keyO):
""" """
#distinguish between a str key and an object (name) key
if isinstance(keyO, str):
return EnhList_SelStr("elem['%s']" % keyO)
else:
return EnhList_SelStr("elem[%s]" % keyO)
#generate comparison methods __lt__ ... __ne__,
#called if "<" ... "!=" are used on self
for operatorT in ( ('__lt__', '<'), ('__le__', '<='), ('__eq__', '=='), \
('__gt__', '>'), ('__ge__', '>='), ('__ne__', '!=') ):
exec( """@classmethod
def {0}(cls, ohs):
return EnhList_SelStr("(elem {1} %s)" % ohs)
""".format(*operatorT))
#called if the 'bitwise and' operator '&' is used on self
@classmethod
def __and__(cls, ohs):
""" As there is no hook for the 'logical and' operator,
the 'bitwise and' is 'abused' instead. """
return EnhList_SelStr("(elem and %s)" % ohs)
#called if the 'bitwise or' operator '|' is used on self
def __or__(cls, ohs):
""" As there is no hook for the 'logical or' operator,
the 'bitwise or' is 'abused' instead. """
return EnhList_SelStr("(elem or %s)" % ohs)
#placeholder class
class Elem(object):
"""
Can be used to create an EnhList_SelStr by using a new notation
comprising attributes, indices, keys, comparison and bitwise and/or
operators, for example:
Elem.a < 5
returns
EnhList_SelStr('elem.a < 5')
- which can be evaluated using eval().
"""
__metaclass__ = EnhList_ElemMeta
#enhanced list
class EnhList(list):
"""
List with enhanced capabilities.
"""
#initialisation
def __init__(self, *params):
""" """
#handle parameters
if len(params) < 2:
#if none or one, keep the behaviour of the underlying
#built-in list type
list.__init__(self, *params)
else:
#if more than one, use them as initial list elements
list.__init__(self, params)
#init lock
self._lock = Lock()
#method called by [key] operation
def elute(self, selS):
"""
Pop all elements from self for which the evaluation of selS
is True ('in place' operation).
'elem' can be used in selS to address each element.
The 'selS' parameter must be a string, which can be eval(uat)ed -
have a look at the description of the helper class EnhList_SelStr.
"""
#ensure, that the parameter is of type str resp. EnhList_SelStr
assert isinstance(selS, str), \
"The parameter 'selS' of the 'elute' method must be a str!"
#lock - (thread) locking mechanism just indicated here / yet
self._lock.acquire(True)
elutedL = EnhList()
indexI = len(self) - 1
while indexI >= 0:
#'elem' is used in selS, see EnhList_ElemMeta.__getattribute__ resp.
#EnhList_ElemMeta.__getitem__ and so on
elem = self[indexI]
#just take elements into account, which fit to the
#evaluation of selS (for which selS is true)
if eval(selS):
elutedL.insert(0, self.pop(indexI))
#next element
indexI -= 1
#unlock
self._lock.release()
#return list with eluted elements
return elutedL
#########################################
### the following just is for testing ###
#########################################
class O(object):
""" """
def __init__(self, val):
""" """
self.a = val
def __repr__(self):
return "a = %s" % self.a
class L(list):
""" """
def __init__(self, val):
""" """
list.__init__(self, (val, val, val))
class D(dict):
""" """
def __init__(self, val):
""" """
dict.__init__(self, a=val)
#main
if __name__ == "__main__":
print ()
#list of objects with attributes
enhList1 = EnhList( range(1,17,2) )
print ( enhList1 )
print ( enhList1.elute( (Elem > 5) & (Elem < 11) ) ) #new notation
print ( enhList1 )
print ()
#list of objects with attributes
enhList2 = EnhList( map(O, range(1,17,2)) )
print ( enhList2 )
print ( enhList2.elute( (Elem.a > 5) & (Elem.a < 11) ) ) #new notation
print ( enhList2 )
print ()
#list of lists
enhList3 = EnhList( map(L, range(1,17,2)) )
print ( enhList3 )
print ( enhList3.elute( (Elem[1] > 5) & (Elem[1] < 11) ) ) #new notation
print ( enhList3 )
print ()
#list of dictionaries
enhList4 = EnhList( map(D, range(1,17,2)) )
print ( enhList4 )
print ( enhList4.elute( (Elem['a'] > 5) & (Elem['a'] < 11) ) ) #new notation
print ( enhList4 )
print ()
Thanks and Cheers, Dominik