22

How can I make a python script change itself?

To boil it down, I would like to have a python script (run.py)like this

a = 0
b = 1
print a + b
# do something here such that the first line of this script reads a = 1

Such that the next time the script is run it would look like

a = 1
b = 1
print a + b
# do something here such that the first line of this script reads a = 2

Is this in any way possible? The script might use external resources; however, everything should work by just running the one run.py-file.

EDIT: It may not have been clear enough, but the script should update itself, not any other file. Sure, once you allow for a simple configuration file next to the script, this task is trivial.

14
  • Yes it is possible. In fact there are several ways of achieving that goal. Commented Sep 22, 2016 at 15:47
  • 1
    Yes, it is possible, but why ? Commented Sep 22, 2016 at 15:50
  • 2
    You can open up your script with open('run.py','w')... and make alterations to it, if that's what you want. Is that all you wanted to know? Commented Sep 22, 2016 at 15:50
  • The script name is in __file__ (see stackoverflow.com/questions/4152963/…) and you can open it like any other file. However, if all you want to do is to save data between runs there are much better ways to do it. Commented Sep 22, 2016 at 15:51
  • 1
    @khelwood: If you open the original as write then how will the OP get the lines to write? The script need to be opened as read, then copied line-by-line to a new file (opened as write) altering those lines required. Then rename the copy. Commented Sep 22, 2016 at 16:00

4 Answers 4

14

For an example (changing the value of a each time its run):

a = 0
b = 1
print(a + b)

with open(__file__, 'r') as f:
    lines = f.read().split('\n')
    val = int(lines[0].split('=')[-1])
    new_line = 'a = {}'.format(val+1)
    new_file = '\n'.join([new_line] + lines[1:])

with open(__file__, 'w') as f:
    f.write(new_file)
Sign up to request clarification or add additional context in comments.

Comments

8

What you're asking for would require you to manipulate files at the {sys} level; basically, you'd read the current file in, modify it, over-write it, and reload the current module. I played with this briefly because I was curious, but I ran into file locking and file permission issues. Those are probably solvable, but I suspect that this isn't really what you want here.

First: realize that it's generally a good idea to maintain a separation between code and data. There are exceptions to this, but for most purposes, you'll want to make the parts of your program that can change at runtime read their configuration from a file, and write changes to that same file.

Second: idomatically, most python projects use YAML for configuration

Here's a simple script that uses the yaml library to read from a file called 'config.yaml', and increments the value of 'a' each time the program runs:

#!/usr/bin/python
import yaml

config_vals = ""
with open("config.yaml", "r") as cr:
   config_vals = yaml.load(cr)

a = config_vals['a']
b = config_vals['b']
print a + b

config_vals['a'] = a + 1
with open("config.yaml", "w") as cw:
   yaml.dump(config_vals, cw, default_flow_style=True)

The runtime output looks like this:

$ ./run.py
3 
$ ./run.py
4
$ ./run.py
5 

The initial YAML configuration file looks like this:

a: 1
b: 2

1 Comment

Actually, the thing you're describing in the first paragraph is exactly what I want.
2

Make a file a.txt that contains one character on one line:

0

Then in your script, open that file and retrieve the value, then immediately change it:

with open('a.txt') as f:
    a = int(f.read())
with open('a.txt', 'w') as output:
    output.write(str(a+1))
b = 1
print a+b

On the first run of the program, a will be 0, and it will change the file to contain a 1. On subsequent runs, a will continue to be incremented by 1 each time.

Comments

0

Gerrat's code but modified.

#some code here
a = 0
b = 1

print(a + b)

applyLine = 1#apply to wich line(line 1 = 0, line 2 = 1)
with open(__file__, 'r') as f:
  lines = f.read().split('\n')#make each line a str in a list called 'lines'
  val = int(lines[applyLine].split(' = ')[-1])#make an int to get whatever is after ' = ' to applyed line 
  new_line = 'a = {}'.format(val+1)#generate the new line
lines[applyLine] = new_line#update 'lines' to add the new line
write = "\n".join(lines)#create what to rewrite and store it in 'write' as str
with open(__file__, 'w') as f:
  f.write(write)#update the code

2 Comments

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
This does not provide an answer to the question. Once you have sufficient reputation you will be able to comment on any post; instead, provide answers that don't require clarification from the asker. - From Review

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.