2

我希望能够在每次运行时自行散列。这是否可能而不必提供脚本的路径?我可以看到两种方法来做到这一点。第一种方法是散列源 Python 文本文件。第二种方法是对编译后的字节码进行哈希处理。

我认为自己选择了 2,因此提出了其他几个问题:

  1. 脚本能否确定其编译字节码在脚本中的位置?
  2. 我将在一个单独的问题中问这个问题。
4

2 回答 2

7

python 脚本可以通过以下方式找出自己的路径:

import os

path = os.path.abspath(__file__)

之后您可以打开源文件并通过hashlib.md5.

脚本文件没有编译的字节码文件;只有模块可以。

请注意,在 Python 2 中,__file__路径使用实际加载的文件的扩展名;对于模块,这是.pyc.pyo仅当有一个缓存的字节码文件准备好被重用时。如果.pyPython 必须编译字节码,要么是因为不存在字节码文件,要么是因为字节码文件过时。

您必须考虑到您的代码是使用命令行开关调用的,这些开关会改变 Python 加载的字节码;如果给定了-O-OO开关,或者PYTHONOPTIMIZE设置了环境标志,Python 将加载或编译到.pyo文件中。

于 2013-10-16T20:22:14.657 回答
1

一种可能的(未经测试的)解决方案是使用反汇编模块dis.dis()将 python 类或模块(但不是实例)转换为汇编语言。具有不同类名的两个相同编写的类看起来相同,但这可以通过cls.__name__在通过 md5 运行组合字符串之前添加来修复

注意dis.dis()打印到标准输出而不是返回一个字符串,因此还有使用 StringIO 捕获打印输出的附加步骤

_

_ >>> import dis, md5
_ >>> class A(object): 
_ ...   def __init__(self, item): print "A(%s)" % item
_ ... 
_ >>> dis.dis(A)
_ Disassembly of __init__:
_   2           0 LOAD_CONST               1 ('A(%s)')
_               3 LOAD_FAST                1 (item)
_               6 BINARY_MODULO       
_               7 PRINT_ITEM          
_               8 PRINT_NEWLINE       
_               9 LOAD_CONST               0 (None)
_              12 RETURN_VALUE        
_ 
_ >>> class B(A):
_ ...   def __init__(self, item): super(A, cls).__init__(item); print "B(%s)" % item
_ ... 

_ >>> dis.dis(B)
_ Disassembly of __init__:
_   2           0 LOAD_GLOBAL              0 (super)
_               3 LOAD_GLOBAL              1 (A)
_               6 LOAD_GLOBAL              2 (cls)
_               9 CALL_FUNCTION            2
_              12 LOAD_ATTR                3 (__init__)
_              15 LOAD_FAST                1 (item)
_              18 CALL_FUNCTION            1
_              21 POP_TOP             
_              22 LOAD_CONST               1 ('B(%s)')
_              25 LOAD_FAST                1 (item)
_              28 BINARY_MODULO       
_              29 PRINT_ITEM          
_              30 PRINT_NEWLINE       
_              31 LOAD_CONST               0 (None)
_              34 RETURN_VALUE        
_ 
_ >>> class Capturing(list):
_ ...     def __enter__(self):
_ ...         self._stdout = sys.stdout
_ ...         sys.stdout = self._stringio = StringIO()
_ ...         return self
_ ...     def __exit__(self, *args):
_ ...         self.extend(self._stringio.getvalue().splitlines())
_ ...         del self._stringio    # free up some memory
_ ...         sys.stdout = self._stdout
_ ... 
_ >>> with Capturing() as dis_output: dis.dis(A)
_ >>> A_md5 = md5.new(A.__name__ + "\n".join(dis_output)).hexdigest()
_ '7818f1864b9cdf106b509906813e4ff8'
于 2017-11-25T12:32:49.443 回答