为python脚本指定布尔过滤器表达式

时间:2011-09-09 04:11:41

标签: python

我有一个包含学生信息的CSV(逗号分隔值)文件。列标题看起来像StudentId,StudentFirstName,StudentLastName,StudentZipCode,StudentHeight,StudentCommuteMethod等,后续行包含各个学生的信息。现在,我想编写一个python 2.5脚本,它将过滤条件作为命令行参数,并返回与此过滤条件匹配的学生(行)集。例如,过滤条件可能如下所示(使用伪代码格式):

"StudentCommuteMethod = Bus AND StudentZipCode = 12345" 

并且可以调用python脚本:

MyPythonScript.py -filter "<above string>" -i input.csv

这应该返回居住在邮政编码为12345且乘坐公共汽车的区域内的所有学生(行)的列表。过滤器也可以是任意复杂的,并且可以包括任意数量的AND,OR运算符。

问题:

  1. 此程序可以让用户指定过滤条件的最佳格式是什么(作为命令行参数)。对于简单表达式,格式应该很简单,并且必须足够强大以表达所有类型的条件。

    • 我想到的格式是(1)SQL,以及(2)python语言本身。在任何一种情况下,我都不知道如何让python在运行时应用这些过滤器。也就是说,如何在命令行中输入表达式并将其应用于行以获得true或false?
  2. 我希望有一个用于以可视方式表达过滤条件的UI。也许是允许每行输入一个简单的双操作数条件的东西,以及使用AND和OR组合它们的一些有用的方法。它应该能够以上面(1)所确定的格式发出过滤器表达式。是否有一些我可以重用的开源项目?

  3. 如果您认为有更好的方法来解决此问题而不是传递命令行表达式+ UI,请随意提及它。最后,用户(对编程不太了解的电气工程师)应该能够轻松输入过滤器表达式。

  4. 谢谢!

    注意:我无法控制输入或输出格式(两个csv文件)。

2 个答案:

答案 0 :(得分:1)

你肯定试图在Python中重新实现SQL。我相信最好使用关系数据库并运行SQL查询。

但是,关于问题1,您可以轻松地让用户在每行数据上输入Python表达式并eval()

这是一个工作示例,它使用exec将列值绑定到局部变量(一个讨厌的黑客,我承认)。为简洁起见,省略了CVS解析。

import optparse, sys

# Assume your CSV data is read into a list of dictionaries
sheet = [
    {'StudentId': 1, 'StudentFirstName': 'John', 'StudentLastName': 'Doe', 'StudentZipCode': '12345', 'StudentCommuteMethod': 'Bus'},
    {'StudentId': 2, 'StudentFirstName': 'Bob', 'StudentLastName': 'Chen', 'StudentZipCode': '12345', 'StudentCommuteMethod': 'Bus'},
    {'StudentId': 3, 'StudentFirstName': 'Jane', 'StudentLastName': 'Smith', 'StudentZipCode': '12345', 'StudentCommuteMethod': 'Train'},
    {'StudentId': 4, 'StudentFirstName': 'Dave', 'StudentLastName': 'Burns', 'StudentZipCode': '45467', 'StudentCommuteMethod': 'Bus'},
]

# Options parsing
parser = optparse.OptionParser()
parser.add_option('--filter', type='string', dest='filter')
options, args = parser.parse_args()

# Filter option is required
if options.filter is None:
    print >> sys.stderr, 'error: no filter expression given'
    sys.exit(1)

# Process rows and build result set
result = []
for row in sheet:
    # Bind each column to a local variable (StudentId, StudentFirstName, etc.);
    # this allows evaluating Python expressions on a row, for example:
    # 'StudentCommuteMethod = "Bus" and StudentZipCode = "12345"'
    for col, val in row.iteritems():
        exec '%s = %s' % (col, repr(val))

    # Apply filter to the row
    if eval(options.filter):
        result.append(row)

# Print out result set
for row in result:
    print row

我使用以下过滤器表达式对其进行了测试:

./MyPythonScript.py --filter 'StudentCommuteMethod == "Bus" and StudentZipCode == "12345"'
./MyPythonScript.py --filter 'StudentCommuteMethod == "Bus" or StudentZipCode == "12345"'

(从命令行运行程序时要注意shell引用规则。)

答案 1 :(得分:1)

这是Danilo的建议略有不同。您可以通过将本地字典传递给exec来避免eval为每行绑定变量,并且csv.DictReader返回的dicts可以很好地用于此:

import csv, optparse
infile = open('datafile.csv')
reader = csv.DictReader(infile)

parser = optparse.OptionParser()
parser.add_option('--filter', type='string', dest='filter')
options, args = parser.parse_args()

for row in reader:
    if eval(options.filter, row):
        print row

这假定输入文件的第一行包含列标题,并且要在表达式中使用的任何标题都必须是有效的Python标识符。