getting around immutable string python - python

Ok, I started coding this:
lastentry = 'first'
campdata = {'a1'=0,
'b2'=0}
class Someclass:
def on_window1_destroy(self, widget, data=None):
print campdata
def func1(self)
lastentry = 'b2'
def func2(self)
lastentry = 'a1'
def func2(self)
campdata[lastcall] +=1
But then I found out that python strings (and integers) were immutable...
So how do I get around ths?

I guess your problem is that you want to change the value of the global variable lastentry by calling func1 or func2, which doesn't work. The reason it does not work is because the variable is in the global scope, and assigning to the same name inside of a function just creates a local variable with the same name as the global one. To assign to a global variable, you need to declare it as such:
lastentry = "something"
def func1():
global lastentry #tell python to treat references to lastentry as global
lastentry = "somethingelse"
Note that you don't need to do this if all you are doing with the global value is reading it like in your third function. But if you assign to a variable, it is treated as local to its scope - which is normally the surrounding function - if you don't explicitly declare it global (or nonlocal in python3).
Global variables should only be used when neccessary as they add complexity to the code. In your case you can probably refactor your code to use an instance variable or class variable for lastentry instead of a global one.

Like others have said, there doesn't seem to be any attempt to modify a string in your code, so that's hardly the problem.
That said, lastcall looks random, should it perhaps be lastentry?

I don't see any problem with your code (except for some details). String immutability does not seem to be a problem, here.
You may want to write, instead of the code in your question:
campdata = {'a1': 0, # Not "= 0"
'b2': 0}
and
campdata[lastentry] +=1 # not "lastcall"
Also, as l4mpi mentioned, you need a global lastentry in the methods where it is modified.
Another point: relying on global variables is quite unusual. If at all possible, it would be best to use instance attributes (self.campdata, self.lastentry).

Related

Passing a variable into a parameter defined by another function python

I am unsure of why the variable totalspeed variable is not being passed correctly to the function startgame as the startgame function is called after the gettotalspeed function.
Exerpt from call function:
gettotalspeed(party_ids)
NoOfEvents=0
startgame(party_ids,totalspeed,distance,NoOfEvents)
Functions
def gettotalspeed(party_ids):
#Get selected party members IDS
print(party_ids)
#Obtain Speeds
ids_string = ','.join(str(id) for id in party_ids)
mycursor.execute("SELECT startspeed FROM characters WHERE CharID IN ({0})".format(ids_string))
myspeeds=mycursor.fetchall()
totalspeed=0
for speedval in myspeeds:
totalspeed=totalspeed + speedval[0]
print("totalspeed is: ",totalspeed)
return totalspeed
def startgame(party_ids,totalspeed,distance,NoOfEvents):
#Check if game end
print(totalspeed)
while distance!=0:
#Travel...
distance=distance-totalspeed
NoOfEvents=NoOfEvents+1
#Generate Random Encounter
genevent(NoOfEvents)
return NoOfEvents
Error Produced:
NameError: name 'totalspeed' is not defined
Outputs (ignoring party_ids)
totalspeed is: 15
I suspect that your problem is self-evident from the main program:
gettotalspeed(party_ids)
NoOfEvents=0
startgame(party_ids,totalspeed,distance,NoOfEvents)
Of the variables you pass to your functions, only NoOfEvents is defined. party_ids, totalspeed, and distance have no definitions.
Work through a tutorial on Python scoping rules. Most of all, note that a function defines a scoping block. Variables inside the function are reclaimed when you leave the function; their names do not apply outside of that block. Your posted program has three independent totalspeed variables.
You forgot to make totalspeed a global variable like global totalspeed in your gettotalspeed() function. You might also be confused about what return does. If you wanted to do it the "proper" way, you could do totalspeed = gettotalspeed(party_ids). Hope this helps!

Python class is changing global variable without declaring it global

I made an error while defining a function within a class, yet it doesn't change how the code operates during runtime.
The error I made was using a global variable when I mean to use an instanced variable.
What I meant to write was:
self._map_data[screen_pos_layer][y][x] = selected_material
Instead, I wrote:
map_data[screen_pos_layer][y][x] = selected_material
However, the intended functionality (change the color of an LED) doesn't change, no matter if it's the instanced variable or the global variable. The function that actually writes the color into the LED is in a different class.
I thought that could only happen if I include global <variable> ? I have very little experience with Python but I'm sure this was true.
class Tools(object):
def __init__(self, _map_data):
self._map_data = _map_data
def paint(self, event):
if selected_tool == select_paint and selected_color != -1:
for j in range(cursor_size_y):
for i in range(cursor_size_x):
y = screen_pos_y + cursor_pos_y + j
x = screen_pos_x + cursor_pos_x + i
map_data[screen_pos_layer][y][x] = selected_material
else:
return
moveCursor(event)
tools = Tools(map_data)
# this is a Tkinter object
window.bind_all("<Control-Up>", tools.paint)
I tried searching for this but I could only find posts about people wanting to use a global variable in a class, I specifically am trying not to.
Evidently your code has previously created a global map_data variable, so the code you're showing is not re-creating the map_data variable, it is only modifying an element of the existing variable. The existing variable is found via regular name lookup, and so in this case it is found in the global context.

get and set value in python

I want to have few global variables in my python code.
Then set their values with set function and want to get their values through get function.
For example:
a = None #global variable1
b= None #global variable2
def set(var, value):
var = value
def get(var):
return var
set(a, '1')
get(b, '2')
I want to have a generic get and set function which will do this for any global variable. How can I do this in python ? The code written here gives error.
If you're willing to pass the variable names as strings, you can do this using the globals() function. But even though you can, I don't think it's a good idea.
First, here's how you could do it:
def get_var(var_name):
return globals()[var_name]
def set_var(var_name, value):
globals()[var_name] = value
I'm not using the name set since that's a builtin type, which you probably don't want to shadow with something else.
You'd call it like:
set_var("foo", 1)
bar = get_var("foo")
print(foo, bar) # prints 1 1
So it works, but there's really no need for those functions. You can assign or fetch global variables using normal syntax instead. Just use foo = 1 instead of set_var("foo", 1) (if you're doing it in a function, put global foo first). Getting a global variable is as simple as naming it (like I do in print(foo)).
If you don't know the variable name ahead of time, you should probably be putting the name and value into a dictionary, rather than making them global variables. Variable names are for the programmer to use. They're not data!
Python is "call by value" not "call by reference". Meaning when you call "set(a, '1')", you are passing the value of "a" to the function "set". The variable "var" in "set" is a local variable to the function itself and will not effect your global variable "a". You need to give your setter and getter methods access to your global variables. You can do this by making them all part of a class object.

Python: Understanding global a little bit more

I have a program in python. part of the program is:
suggestengines = get_suggestengines(suggestengines)
sleeptimer = sleepcount * len(suggestengines)
seeds = get_seeds(dummydata=False)
For further programming I want to make a function of it:
def first_step():
suggestengines = get_suggestengines(suggestengines)
sleeptimer = sleepcount * len(suggestengines)
seeds = get_seeds(dummydata=False)
Now I get an error for "suggestengines" that I want to pass into get_suggestengines(). Also sleep timer and seeds get a marker, that I don't use them in the rest of the program. I googled it and got the answer: Us global. So I added global for everything
def first_step():
global suggestengines
global sleeptimer
global seeds
suggestengines = get_suggestengines(suggestengines) #which engines to run?
sleeptimer = sleepcount * len(suggestengines)
seeds = get_seeds(dummydata=False)
In further part of the program I have
for seed in tqdm(seeds, leave=True):
there the program gives me an error vor seeds in tqdm. If I change it to also make a def of it like:
def partTwo():
for seed in tqdm(seeds, leave=True):
Then I don't get an error anymore although I didn't used global. Can someone explain me why and if I need to use global in part 2 also?
The statement
global <identifier>
tells python that <identifier> should refer to a global when used in assignments. This is necessary in functions that change globals because Python has no syntactical difference between declaring a variable and assigning to an existing variable. The default in python is to have assignments in functions create new variables, rather than change global state.
When you just read from a variable there is no syntactic ambiguity, so Python will just use whatever variable it finds (i.e. global if there is no local one).
Example:
a = 1
def foo():
a = 2 # this will create a new, local variable a
def bar():
global a # "when I refer to a, I mean the global one"
a = 2 # this will change the global variable a
If no global with the specified name exists, the global statement itself will not create a new global variable, but any following assignment will. E.g. given the following:
def x():
global c
def y():
global c
c = 1
def z()
print c
x(); z() would be an error(global name 'c' is not defined), while y(); z() would print 1.
seeds hasn't been initialized yet by the time the for loop is hit, since its initialization is part of a def that hasn't been called yet. If you put the for loop inside a def then it will be called in the order you call the functions, so the interpreter won't complain until you actually use it.
The only thing to keep in mind here is this: use variables after they have been initialized.

Conditionally modify global variable

I'd like to do something like this, but I get a SyntaxWarning and it doesn't work as expected
RAWR = "hi"
def test(bool):
if bool:
RAWR = "hello" # make RAWR a new variable, don't reference global in this function
else:
global RAWR
RAWR = "rawr" # reference global variable in this function
print RAWR # if bool, use local, else use global (and modify global)
How do I get this to work? Passing in True or False modifies the global variable.
You cannot. Within a scope, a specific name refers either to a local variable, or to a non-local (e.g. global, or from an outer function) variable. Not both. The global RAWR line makes RAWR a global for the entire scope (that's why you get a warning, it doesn't do what you think it does), just like assignment to a variable makes it local for the entire scope. Edit: Thanks to veredesmarald, we now know it is in fact a syntax error in Python 2. This half of my answer only applies to Python 3 apparently.
You should just use a differently-named local variable, and in the branch where you want to "promote" it to a global, set the global and the local variable. (Or just don't use globals at all.)
The only easy way you can go would be
RAWR = "hi"
def test(newone):
if newone:
lR = "hello" # make RAWR a new variable, don't reference global in this function
else:
global RAWR
lR = RAWR # reference global variable in this function
print lR # if bool, use local, else use global (and modify global)
# modify lR and then
if not newone:
RAWR = lR
Another way, however, could be to abuse the concept of classes and objects to your purposes.
class store_RAWR(object):
RAWR = "hi"
def __init__(self, new): self.RAWR = new
def test(newone):
if newone:
myR = store_RAWR("hello") # get a (temporary) object with a different string
else:
myR = store_RAWR # set the class, which is global.
# now modify myR.RAWR as you need
But this requires other program parts which use the global name to be changed as well.

Categories

Resources