Python中的逻辑错误'if'评估

我刚刚发生了最奇怪的错误。 我没有机会完全debugging它,但我想发布这个,看看是否有其他人有类似的问题。

问题

下面的代码是在一个函数下载一个文件。 if最终文件存在, if语句逻辑将跳过下载步骤。

 log.debug('force: {}, isfile: {}'.format(force, os.path.isfile(fasta_path))) log.debug('if result: {}'.format(force or not os.path.isfile(fasta_path))) if force or not os.path.isfile(fasta_path): # we don't have the file, so download it log.info('Downloading reference FASTA') else: log.info('FASTA found--skipping download.') 

运行时,打印出下列内容:

 [17-02-14 05:03:32 - __main__:119 - DEBUG] force: False, isfile: True [17-02-14 05:03:32 - __main__:120 - DEBUG] if result: False [17-02-14 05:03:32 - __main__:124 - INFO] Downloading reference FASTA 

分解

forcevariables旨在允许用户“强制”下载,而不pipe现有数据如何。 正如你在输出中看到的那样,它是False

文件path有效,文件存在,如输出中所示。

因此,if语句本质上是False or not True ,应该(也是在输出中)评估为False ; 然而, True条件( Downloading reference )被执行。

我曾尝试括号和一些最小的debugging,但我找不到任何理由发生。 例如,以下在python解释器中按预期工作:

 if False or not True: print('hi') else: print('bye') 

环境

在Docker容器中,ubuntu 16.04上的Python 3.5会发生此问题。

正如@ user2357112在注释中指出的那样,force很可能是一个string:

 >>> print('if result: {}'.format("False" or not True)) if result: False >>> "False" or not True 'False' >>> type("False" or not True) <class 'str'> >>> bool("False" or not True) True 

这可能是为什么第二个debugging语句显示False(因为它显示的是string而不是布尔值)。 你可以testing这个

 log.debug('if result: {}'.format(bool(force or not os.path.isfile(fasta_path)))) 

if result: True预期输出)或

 log.debug('if result: {}'.format(repr(force or not os.path.isfile(fasta_path)))) 

if result: 'False'期望的输出if result: 'False' – 注意引号是一个string而不是布尔值)

编辑说明:按照4.2。 布尔操作expression式x or y根据if x is false, then y, else x 。 从4.1开始。 真值testing只有空序列是False (在真值testing中)序列'False'True 。 因此'False' or x评估string'False' (独立于x的值)。 打印期间, 'False'不被解释为布尔值。 然而,在if语句中,string'False'被testing为Truth

FWIW,在Windows机器上运行的代码稍作修改,Python 2.6.5:

 def stackOverflow(self, force, fasta_path): logging.info('force: {0}, isfile: {1}'.format(force, os.path.isfile(fasta_path))) logging.info('if result: {0}'.format(force or not os.path.isfile(fasta_path))) if force or not os.path.isfile(fasta_path): # we don't have the file, so download it logging.info('Downloading reference FASTA') else: logging.info('FASTA found--skipping download.') 

这需要force的预期path(“跳过下载”)为False ,path存在。 电话是:

 stackOverflow(False, r'C:\temp\pythoncode.txt') 

正如注释所暗示的,将第一个参数作为一个string将采用神秘的path(“下载”)。

 stackOverflow("False", r'C:\temp\pythoncode.txt')