17

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.

ggorlen
  • 44,755
  • 7
  • 76
  • 106
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • Yes it is possible. In fact there are several ways of achieving that goal. – Tomasz Plaskota Sep 22 '16 at 15:47
  • 1
    Yes, it is possible, but why ? – Alex Sep 22 '16 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? – khelwood Sep 22 '16 at 15:50
  • The script name is in `__file__` (see http://stackoverflow.com/questions/4152963/get-the-name-of-current-script-with-python) 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. – cdarke Sep 22 '16 at 15:51
  • @khelwood: opening the script with `w` access will result in it being zero length! – cdarke Sep 22 '16 at 15:54
  • @cdarke Only if you don't write anything new to it. I didn't say "just open it and don't write anything." – khelwood Sep 22 '16 at 15:58
  • If it can use external resources, just make a file that contains the character `0`, then have your script get the value of `a` from that file and rewrite the file to change it to `1`. – TigerhawkT3 Sep 22 '16 at 15:58
  • 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. – cdarke Sep 22 '16 at 16:00
  • @cdarke If the question is "How can I alter the script?" then you can do that by opening the file in write mode and writing new content to it. The question doesn't say you have to read the old content. That's just your idea of how you would do it. – khelwood Sep 22 '16 at 16:04
  • @khelwood: The question *does* say the old content s used. Only one value is altered, the rest is the same. – cdarke Sep 22 '16 at 16:08
  • @cdarke I respectfully disagree. – khelwood Sep 23 '16 at 08:04
  • I can think of a few cases where modifying code on-the-fly can be useful, though I'd personally avoid structuring it to modify the main run file. – Matt Thompson Sep 20 '17 at 00:04
  • @ImportanceOfBeingErnest wondering if you found a better way to do this? With machine learning capabilities I find it hard to believe there isn't a better way than simply modifying text and re-writing files...? – Derek Eden Jan 17 '20 at 15:11
  • This is usually only important in production, but bear in mind that on *nix you need `chmod 777 file.py` to allow the program to modify itself when not run by the owner. – kettle Jul 07 '21 at 05:41

4 Answers4

11

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)
Gerrat
  • 28,863
  • 9
  • 73
  • 101
5

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
Joe
  • 800
  • 4
  • 15
1

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.

TigerhawkT3
  • 48,464
  • 6
  • 60
  • 97
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
gz200
  • 1
  • 2
  • 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](/help/how-to-answer). – Community Jan 21 '23 at 03:48
  • This does not provide an answer to the question. Once you have sufficient [reputation](https://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](https://stackoverflow.com/help/privileges/comment); instead, [provide answers that don't require clarification from the asker](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). - [From Review](/review/late-answers/33664972) – Michael Katt Jan 26 '23 at 10:21