【问题标题】:self modifying python script自我修改python脚本
【发布时间】:2017-12-23 20:16:48
【问题描述】:

我想创建 python 脚本,它可以使用Python Language Services 或任何其他方式修改该脚本本身中的代码。
例如跟踪其成功执行次数的脚本

import re
COUNT = 0

def updateCount():
    # code to update second line e.g. COUNT = 0
    pass

if __name__ == '__main__':
    print('This script has run {} times'.format(COUNT))
    updateCount()

成功执行此脚本后,代码应更改为

import re
COUNT = 1

def updateCount():
    # code to update second line e.g. COUNT = 0
    pass

if __name__ == '__main__':
    print('This script has run {} times'.format(COUNT))
    updateCount()

我想到的简单方法是在写入模式下打开__file__,并使用调节器表达式等进行必要的修改。但这没有用,我得到了异常io.UnsupportedOperation: not readable。即使这种方法可行,也会非常冒险,因为它会破坏我的整个脚本。所以我正在寻找使用 Python 语言服务的解决方案。

【问题讨论】:

  • 您可以打开和编辑__file__
  • 请注意,您可能会遇到权限问题(如果脚本是由没有写入权限的用户运行的)。
  • 这不起作用io.UnsupportedOperation: not readable
  • 您可能正在寻找eval 函数。您可以将 Python 源代码构建为文本并在您自己的模块或其他上下文中运行它。编辑你自己的 .py 文件是可能的,作为一个有趣的实验:)
  • 将控件值写入其他文件。

标签: python self-modifying


【解决方案1】:

@kyriakosSt 的答案有效,但硬编码COUNT 的分配必须在第二行,当由于源被修改为其他内容而导致行号更改时,随着时间的推移可能容易出现意外行为。

要获得更强大的解决方案,您可以改用lib2to3 来解析和更新源代码,方法是将lib2to3.refactor.RefactoringTool 子类化以使用作为lib2to3.fixer_base.BaseFix 子类的修复程序重构代码带有'COUNT' '=' any 模式的表达式语句,以及通过增加其整数值来更新最后一个子节点的transform 方法:

from lib2to3 import fixer_base, refactor

COUNT = 0 # this should be incremented every time the script runs

class IncrementCount(fixer_base.BaseFix):
    PATTERN = "expr_stmt< 'COUNT' '=' any >"

    def transform(self, node, results):
        node.children[-1].value = str(int(node.children[-1].value) + 1)
        return node

class Refactor(refactor.RefactoringTool):
    def __init__(self, fixers):
        self._fixers = [cls(None, None) for cls in fixers]
        super().__init__(None)

    def get_fixers(self):
        return self._fixers, []

with open(__file__, 'r+') as file:
    source = str(Refactor([IncrementCount]).refactor_string(file.read(), ''))
    file.seek(0)
    file.write(source)

演示:https://repl.it/@blhsing/MushyStrangeClosedsource

【讨论】:

    【解决方案2】:

    是的,您可以使用语言服务来实现自我修改,如下例:

    >>> def foo(): print("original foo")
    >>> foo()
    original foo
    >>> rewrite_txt="def foo(): print('I am new foo')"
    >>> newcode=compile(rewrite_text,"",'exec')
    >>> eval(newcode)
    >>> foo()
    I am new foo
    

    因此,通过新的动态生成代码,您可以替换原始源文件中包含的内容,而无需修改文件本身。

    【讨论】:

    • 好答案:使用compile() + eval(),可以实现无限自修改代码。即,您将一个简单的程序传递给您的朋友,但通过这种自我修改机制,有可能将自己变成病毒/恶意程序。
    【解决方案3】:

    python 脚本只不过是一个文本文件。因此,您可以将其作为外部文件打开并对其进行读写。 (使用__file__ 变量可以获得脚本的确切名称):

    def updateCount():
        fin = open(__file__, 'r')
        code = fin.read()
        fin.close()
    
        second_line = code.split('\n')[1]
        second_line_parts = second_line.split(' ')
        second_line_parts[2] = str(int(second_line_parts[2])+1)
    
        second_line = ' '.join(second_line_parts)
        lines = code.split('\n')
        lines[1] = second_line
        code = '\n'.join(lines)
    
        fout = open(__file__, 'w')
        fout.write(code)
        fout.close()
    

    【讨论】:

    • 我在发布我的问题之前尝试了这个。这不起作用并给出io.UnsupportedOperation: not readable
    • 这会将整个文件加载到内存中,因为fout.read() 是不必要的......此外,使用with 正确打开和关闭文件会更明智......除此之外,很好的答案!
    • @AlokSinghMahor 如果您尝试过但没有成功,您为什么不在问题中写下?
    • @AlokSinghMahor 事实是,很多人都在为你提供你已经尝试过但失败的解决方案......你不得不重复向他们解释,所以很明显你的问题中缺少一些东西
    • @OferSadan:我在我的问题中提到过,谢谢
    猜你喜欢
    • 2022-06-10
    • 2012-09-25
    • 2020-01-20
    • 1970-01-01
    • 1970-01-01
    • 2023-03-10
    • 2022-12-15
    • 2015-11-22
    • 2014-09-23
    相关资源
    最近更新 更多