将逻辑运算符(和部分表达式)传入函数参数 Python
Pass in logical operators (and partial expressions) into a function argument Python
在 SQLAlchemy 中,可以这样做:
mytable.query.filter(mytable.some_col < 5).all()
我怎样才能实现类似的东西?我希望开发人员用户能够将逻辑操作传递给函数。这是一个例子:
class row_obj:
def __init__(self, val1, val2, val3, val4):
self.val1 = val1
self.val2 = val2
self.val3 = val3
self.val4 = val4
def __repr__(self):
return str(self.val1)+","+str(self.val2)+","+str(self.val3)+","+str(self.val4)
class table_obj:
""" takes a list of row_objs """
def __init__(self, rows):
self.rows = rows #rows is a list of row_obj
def __repr__(self):
return "\n".join([str(row) for row in self.rows])
def filter(self, condition):
# I would like to return all rows which meet the condition here
return table_obj([row for row in self.rows if condition])
a = table_obj([ row_obj(1,2,3,4),
row_obj(5,6,7,8),
row_obj(2,4,6,8),
row_obj(5,2,7,4)])
print a.filter(row_obj.val3 == 7)
#should return
#5,6,7,8
#5,2,7,4
您可以通过 lambda 函数来执行此操作;然后将它们作为任何正常函数处理:
>>> def someFunction (condition):
return [row for row in range(100) if condition(row)]
>>> someFunction(lambda x: x % 11 == 0)
[0, 11, 22, 33, 44, 55, 66, 77, 88, 99]
>>> someFunction(lambda x: x % 23 == 0)
[0, 23, 46, 69, 92]
当然,除了像我的示例那样传递一个简单的 int 对象,您还可以传递一个更复杂的对象并对其进行一些更复杂的检查,例如:
lambda x: x.someOtherProperty.evenAMethod('with a parameter') > 42
对于您问题中的具体示例:更改 table_obj.filter
以调用 condition
参数并将该行作为参数:
def filter(self, condition):
return table_obj([row for row in self.rows if condition(row)])
# ^^^^^
# new part
然后,您可以使用 lambda 表达式指定过滤器:
print a.filter(lambda row_obj: row_obj.val3 == 7)
# ^^^^^^^^^^^^^^^
# new part
这 完全 给了你想要的结果。
正如您所提到的,SQLAlchemy 允许这样的表达式:
mytable.query.filter(mytable.some_col < 5).all()
这是可能的,因为 mytable.some_col
为 returns 其他对象(在本例中 __lt__
)的每个逻辑运算符实现了魔法方法。所以运算符并没有真正传入。在我的应用程序中,它定义了一个 User
对象,可以通过以下方式看到:
>>> User.first_name == 'John'
<sqlalchemy.sql.elements.BinaryExpression object at 0x10f0991d0>
你可以做同样的事情来达到同样的效果。
不幸的是,这有点不平凡,实施起来有点粗糙,但你可以这样做
import operator
class Comparator:
def __init__(self,fieldName,compareToValue,my_operator):
self.op = my_operator
self.field = fieldName
self.comparedTo = compareToValue
def __call__(self,row):
my_row_val = getattr(row,self.field)
return self.op(my_row_val,self.comparedTo)
class row_obj:
class RowItem:
def __init__(self,name):
self.name = name
def __eq__(self,other):
return Comparator(self.name,other,operator.eq)
val1 = RowItem("val1")
val2 = RowItem("val2")
val3 = RowItem("val3")
val4 = RowItem("val4")
def __init__(self, val1, val2, val3, val4):
self.val1 = val1
self.val2 = val2
self.val3 = val3
self.val4 = val4
def __str__(self):
return str([self.val1,self.val2,self.val3,self.val4])
def __repr__(self):
return str(self)
class MyTable:
def __init__(self,rows):
self.rows = rows
def filter(self,condition):
for row in self.rows:
if condition(row):
yield row
rows = [row_obj(1,2,3,4),row_obj(1,2,7,4),row_obj(1,2,3,4),row_obj(7,7,7,7)]
mytable = MyTable(rows)
print list(mytable.filter(row_obj.val3 == 7))
这更多是为了让您了解您将要走的道路,这将是非常重要的(而且有点恶心......)
在 SQLAlchemy 中,可以这样做:
mytable.query.filter(mytable.some_col < 5).all()
我怎样才能实现类似的东西?我希望开发人员用户能够将逻辑操作传递给函数。这是一个例子:
class row_obj:
def __init__(self, val1, val2, val3, val4):
self.val1 = val1
self.val2 = val2
self.val3 = val3
self.val4 = val4
def __repr__(self):
return str(self.val1)+","+str(self.val2)+","+str(self.val3)+","+str(self.val4)
class table_obj:
""" takes a list of row_objs """
def __init__(self, rows):
self.rows = rows #rows is a list of row_obj
def __repr__(self):
return "\n".join([str(row) for row in self.rows])
def filter(self, condition):
# I would like to return all rows which meet the condition here
return table_obj([row for row in self.rows if condition])
a = table_obj([ row_obj(1,2,3,4),
row_obj(5,6,7,8),
row_obj(2,4,6,8),
row_obj(5,2,7,4)])
print a.filter(row_obj.val3 == 7)
#should return
#5,6,7,8
#5,2,7,4
您可以通过 lambda 函数来执行此操作;然后将它们作为任何正常函数处理:
>>> def someFunction (condition):
return [row for row in range(100) if condition(row)]
>>> someFunction(lambda x: x % 11 == 0)
[0, 11, 22, 33, 44, 55, 66, 77, 88, 99]
>>> someFunction(lambda x: x % 23 == 0)
[0, 23, 46, 69, 92]
当然,除了像我的示例那样传递一个简单的 int 对象,您还可以传递一个更复杂的对象并对其进行一些更复杂的检查,例如:
lambda x: x.someOtherProperty.evenAMethod('with a parameter') > 42
对于您问题中的具体示例:更改 table_obj.filter
以调用 condition
参数并将该行作为参数:
def filter(self, condition):
return table_obj([row for row in self.rows if condition(row)])
# ^^^^^
# new part
然后,您可以使用 lambda 表达式指定过滤器:
print a.filter(lambda row_obj: row_obj.val3 == 7)
# ^^^^^^^^^^^^^^^
# new part
这 完全 给了你想要的结果。
正如您所提到的,SQLAlchemy 允许这样的表达式:
mytable.query.filter(mytable.some_col < 5).all()
这是可能的,因为 mytable.some_col
为 returns 其他对象(在本例中 __lt__
)的每个逻辑运算符实现了魔法方法。所以运算符并没有真正传入。在我的应用程序中,它定义了一个 User
对象,可以通过以下方式看到:
>>> User.first_name == 'John'
<sqlalchemy.sql.elements.BinaryExpression object at 0x10f0991d0>
你可以做同样的事情来达到同样的效果。
不幸的是,这有点不平凡,实施起来有点粗糙,但你可以这样做
import operator
class Comparator:
def __init__(self,fieldName,compareToValue,my_operator):
self.op = my_operator
self.field = fieldName
self.comparedTo = compareToValue
def __call__(self,row):
my_row_val = getattr(row,self.field)
return self.op(my_row_val,self.comparedTo)
class row_obj:
class RowItem:
def __init__(self,name):
self.name = name
def __eq__(self,other):
return Comparator(self.name,other,operator.eq)
val1 = RowItem("val1")
val2 = RowItem("val2")
val3 = RowItem("val3")
val4 = RowItem("val4")
def __init__(self, val1, val2, val3, val4):
self.val1 = val1
self.val2 = val2
self.val3 = val3
self.val4 = val4
def __str__(self):
return str([self.val1,self.val2,self.val3,self.val4])
def __repr__(self):
return str(self)
class MyTable:
def __init__(self,rows):
self.rows = rows
def filter(self,condition):
for row in self.rows:
if condition(row):
yield row
rows = [row_obj(1,2,3,4),row_obj(1,2,7,4),row_obj(1,2,3,4),row_obj(7,7,7,7)]
mytable = MyTable(rows)
print list(mytable.filter(row_obj.val3 == 7))
这更多是为了让您了解您将要走的道路,这将是非常重要的(而且有点恶心......)