自修改python脚本

Alo*_*hor 4 python self-modifying

我想创建python脚本,它可以使用Python语言服务或使用任何其他方式修改该脚本本身的代码.
例如,跟踪其成功执行计数的脚本

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()
Run Code Online (Sandbox Code Playgroud)

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

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()
Run Code Online (Sandbox Code Playgroud)

我想到的简单方法是__file__在写入模式下打开,并使用调节器实验等进行必要的修改.但是这不起作用我得到了例外io.UnsupportedOperation: not readable.即使这种方法有效,也会有很大的风险,因为它会破坏我的整个脚本.所以我正在寻找使用Python语言服务的解决方案.

ddb*_*bug 10

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

>>> 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
Run Code Online (Sandbox Code Playgroud)

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


kyr*_*sSt 6

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()
Run Code Online (Sandbox Code Playgroud)

  • @AlokSinghMahor如果你尝试了它并且它不起作用,你为什么不在你的问题中写这个? (2认同)
  • @AlokSinghMahor 事实上,很多人都在给你提供你已经尝试过但失败的解决方案......你不得不重复向他们解释,所以很明显你的问题中缺少一些东西 (2认同)

blh*_*ing 5

@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)
Run Code Online (Sandbox Code Playgroud)

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