我可以使用Variablized String Literal替换部分Python“If”语句吗?

时间:2017-08-23 01:14:47

标签: python if-statement

我有一个很长的问题,可能是一个非常简短的答案。

我是Python的新手(差不多两周,现在),但我已经使用了很多年的VBScript,所以我理解了许多基本概念。

我在Stack Overflow和互联网上搜索了一个解决方案但却找不到任何东西;我不确定这在Python中是否可行,但如果不是,我会有点惊讶。 我使用Python3编写了一个文件搜索程序,允许用户在他们的计算机上搜索文件。用户可以选择基于几个不同的参数进行搜索:名称,大小范围,日期修改范围,对于非Linux系统,可以选择日期创建的范围。搜索功能对于每个单独的参数以及参数的组合都能很好地工作(顺便说一句,这要归功于我在Stack Overflow上找到的许多答案/讨论)。 我的问题是,实际的搜索相当不优雅,我相信,它的速度可能比它慢。该程序使用标志(不是真正的Python标志,这就是我碰巧称之为),来设置搜索选项。让我用一些伪代码来说明:

    # Variables get their values from user entry
    sName = "test" # string to search for
    sMinSize = 2 # minimum search-by size in MB
    sMaxSize = 15 # maximum search-by size in MB
    sModded1 = 2008-01-23 # earliest modified-by date
    sModded2 = 2017-08-22 # latest modified-by date

    sCreated1 = 2008-01-23 # earliest created-by date
    sCreated2 = 2017-08-22 # latest created-by date

    # Search parameters - choosing one of these changes the value from 0 to 1:
    flagName = 0 # search by name
    flagSize = 0 # search by size
    flagModified = 0 # search by last modified date
    flagCreated = 0 # search by last created date

    for root, dirs, files in os.walk(strPath, followlinks=False):
        for fName in files:
            fileDate = os.path.getmtime(fName)
            fileSize = os.stat(fName).st_size
            if flagName = 1:
                    if fName.find(sName) > 0:
                        do_stuff
            elif flagSize = 1:        
                    if sMinSize < fileSize < sMaxSize:
                        do_stuff
            elif flagName = 1 and flagSize = 1:
                    if fName.find(sName) > 0 and if sMinSize < fileSize < sMaxSize:
                        do_stuff
    ... etc

这仅适用于3种可能的组合 - 总共有14种。虽然输入所有组合并没有问题,但我相信这会严重影响搜索的速度和效率。

我想到了另一种更优雅的解决方案,并且可能执行得更快,但我仍然认为有更好的方法:

    if flagName = 1:
        for root, dirs, files in os.walk(strPath, followlinks=False):
            for fName in files:
                fileDate = os.path.getmtime(fName)
                fileSize = os.stat(fName).st_size
                if fName.find(sName) > 0:
                    do_stuff

    elif flagName = 1 and flagSize = 1:
        for root, dirs, files in os.walk(strPath, followlinks=False):
            for fName in files:
                fileDate = os.path.getmtime(fName)
                fileSize = os.stat(fName).st_size
                if fName.find(sName) > 0 and if sMinSize < fileSize < sMaxSize:
                    do_stuff
    ... etc

同样,这更优雅,(我相信)效率更高,但仍然不理想。 我想做的是根据用户的搜索条件创建一个“if”语句,并使用它来进行搜索(注意在VBScript中可能有类似的东西)。这些陈述将在搜索语句发生之前进行:

可能的选项1:

    if flagName = 1:
        iClause = "fName.find(sName) > 0"
    elif flagName = 1 and flagSize = 1:
        iClause = "fName.find(sName) > 0 and if sMinSize < fileSize < sMaxSize"
    ... etc

可能的选项2:

    flagClause = 0
    if flagName = 1:
        iClause = "fName.find(sName) > 0"
        flagClause = flagClause + 1
    if flagClause = 0
        iClause = "sMinSize < fileSize < sMaxSize"
    else:
        iClause = iClause + "and sMinSize < fileSize < sMaxSize"
        flagClause = flagClause + 1
    ... etc

然后将“iClause”插入我的搜索语句中,如下所示:

    for root, dirs, files in os.walk(strPath, followlinks=False):
        for fName in files:
            fileDate = os.path.getmtime(fName)
            fileSize = os.stat(fName).st_size
            if **iClause**:
                do_stuff

这将简化代码,使其更易于阅读和维护,并且(我相信)使其更加高效和快速。

这可以用Python吗?

修改

我感谢你们所有人花时间阅读我冗长的问题,但我不相信你得到的是我所要求的 - 很可能是因为它(过度)冗长。

我想知道如何实现以下内容:

    a = "sMinSize < fileSize < sMaxSize"
    b = "and sMinSize < fileSize < sMaxSize"
    iClause = a+b

然后将“iClause”插入我的“if”语句中,如下所示:

    if iClause:
        do_stuff

这基本上是将字符串文字转换为变量,然后使用变量化(可能不是真正的单词)字符串文字作为我的语句。 我希望这更清楚。

3 个答案:

答案 0 :(得分:3)

创建一个谓词函数,每个案例一个。确定您正在使用的案例并使用关联的谓词。在列表中收集选定的谓词(或将它们组合成新的谓词),然后在你的循环中应用:

predicates = []
if flagName:
    predicates.append(lambda fileName: fileName.find(sName) > 0)
if flagSize:
    predicates.append(lambda fileName: sMinSize < os.stat(fileName).st_size < sMaxSize)
if flagModified:
    predicates.append(lambda fileName: sModded1 < os.path.getmtime(fileName) < sModded2)
if flagCreated:
    predicates.append(lambda fileName: sCreated1 < os.path.getctime(fileName) < sCreated2)

for root, dirs, files in os.walk(strPath, followlinks=False):
    for fName in files:
        if all(p(fName) for p in predicates):
            # do stuff

根据您的偏好,您可能希望使用命名函数而不是lambda。对于更复杂的场景,您可能希望将它们实现为仿函数。

答案 1 :(得分:1)

这是一些横向思考。如果你寻找符合标准的东西,它们必须全部匹配。但是如果你寻找不匹配的东西,那么取消资格只需要一个错误。所以你不需要编写复杂的查询;只需一次检查一个选项就足够了。你可以循环做到这一点!

# supplied by user (you might want to look into argparse)
options = {
    "name": "jpg"
    "minsize": "1024"
}

# checking code
option_checkers: {
    "name": lambda fName, limit: fName.find(limit) != -1
    "minsize": lambda fName, limit: limit <= os.stat(fName).st_size
    "maxsize": lambda fName, limit: os.stat(fName).st_size < limit
}

def okay(fName, options):
    for option, limit in options.items():
        if not option_checkers[option](fName, limit)
             return False
    return True

for root, dirs, files in os.walk(strPath, followlinks=False):
    for fName in files:
        if okay(fName, options):
            # fits all criteria: do stuff

答案 2 :(得分:-1)

我找到了解决方案 - “eval()”函数。这将完全符合我一直在寻找的。有了这个,我可以定义我的搜索片段并编写几个简短的“if”语句,如下所示:

flagClause = 0
if flagName = 1:
    iClause = "fName.find(sName) > 0"
    flagClause = flagClause + 1
if flagClause = 0
    iClause = "sMinSize < fileSize < sMaxSize"
else:
    iClause = iClause + "and sMinSize < fileSize < sMaxSize"
    flagClause = flagClause + 1
... etc

现在我的搜索字符串已经放在一起,我将其插入我的“for”循环:

for root, dirs, files in os.walk(strPath, followlinks=False):
    for fName in files:
        fileDate = os.path.getmtime(fName)
        fileSize = os.stat(fName).st_size
        if eval(iClause):
            do_stuff

在我的搜索字符串之前“for”循环开始时,它不必离开循环来检查每个条件。 应该是一个相对有效的搜索。

现在......有没有人看到这个解决方案有什么问题?

最终修改:

根据我收到的建议(和告诫),“eval”功能不是我的解决方案。相反,我使用了杰夫建议的方法。他的解决方案更快,更高效,更易于维护。

再次感谢大家的意见和建议!