0

Can anyone explain to me why I can change immutable variable if it's global in Python? I don't understand why is this allowed in global state and not allowed everywhere else. I recently started learning programming and I just don't get it. Example below.

class Ddata():
    def __init__(self, iw, ih, fw, fh):
        self.iw = iw
        self.ih = ih
        self.fw = fw
        self.fh = fh

x = Ddata(1784, 366, 160, 180)

s_h = 0
s_w = 0
c_f = 20

def sp(x, cf, sw, sh):
    sw = x.fw * cf
    print sw
    if sw > x.iw:
        sw -= x.iw
        print sw
        sh = (sh + x.fh) + 4

sp(x, c_f, s_w, s_h)

print s_w
print s_h

print "----"  

def sp_global():
    global s_w
    global s_h
    global c_f
    global x
    s_w = x.fw * c_f
    print s_w
    if s_w > x.iw:
        s_w -= x.iw
        print s_w
        s_h = (s_h + x.fh) + 4

sp_global()

print s_w
print s_h
JeremyK
  • 147
  • 1
  • 1
  • 10
  • Which immutable variables are you worried about? – juanchopanza Nov 07 '15 at 14:31
  • 2
    I don't think any of the variables in your example are immutable. Gobal variables can be changed, function-local variables can be changed, and class object variables can be changed. – Tom Karzes Nov 07 '15 at 14:32

4 Answers4

2

I think you are confusing global variables and function local variable of the same name.
Consider for example

int_data = 15

def some_func():
    int_data = 10   # This now creates a new local variable of same name instead of updating global variable
    print int_data  # prints local variable with value 10

some_func()

This is because in python a new variables are created when any value is assigned to variable not yet available.
To avoid creating a new variable and update the global variable you must explicitly mention using keyword global

shanmuga
  • 4,329
  • 2
  • 21
  • 35
2

Some core things:

  • Name your variables according to what they do, nobody can read your code else
  • Don't use global. Its never necessary, simply not good practice and will give you a hard time to overview your code. I know you did it to show things, but still, just the hint ;)

  • EDIT: And in case this is not clear: Instance Variables (vars of objects/classes) are NOT immutable ;)

Concerning the immutable objects thing:

The concept of immutability is not what you are seeing here. Immutable variables mean variables you cant change after they are created. An example are tuples as you cant change their components afterwards.

What you see here are simply name scopes.

If you change a variable inside a function it changes them in their own local name scope. Though they do not change what is given to them as this would make huge problems (consider a variable like a name given to a function that should give you the first letter if the name. If the function would automatically change the name the program would "forget" it and only have the first letter afterwards). If you want to use the results of a function outside the function return them. If you want to use the concept of classes, where functions (for classes they are called methods) change attributes (variables) of the object (of a class) then you have to declare those functions as methods of the class. In your example you would then call them like this: x.sp(...).

For more on the concept of Classes (Object Oriented Programming) search the web there are plenty good explanations and examples. Its a core concept of programming and very useful to learn!

EDIT: As this didnt seem to be clear I edited your code for two possibilities:

class Ddata():
    def __init__(self, iw, ih, fw, fh):
        self.iw = iw
        self.ih = ih
        self.fw = fw
        self.fh = fh

        self.sh = 0
        self.sw = 0

    def sp(self, cf, sw, sh):
        sw = self.fw * cf
        print (sw)
        if sw > self.iw:
            sw -= self.iw
            print (sw)
            sh = (sh + self.fh) + 4

        self.sh = sh
        self.sw = sw

        return (self.sw, self.sh)



x = Ddata(1784, 366, 160, 180)

s_h = 0
s_w = 0
c_f = 20

result_of_sp = x.sp(c_f, s_w, s_h) #result is a tuple where we store two values


print (result_of_sp[0])
print (result_of_sp[1])

print ("----")  

x = Ddata(1784, 366, 160, 180)

def sp_global():
    global s_w
    global s_h
    global c_f
    global x
    s_w = x.fw * c_f
    print (s_w)
    if s_w > x.iw:
        s_w -= x.iw
        print (s_w)
        s_h = (s_h + x.fh) + 4

sp_global()

print (s_w)
print (s_h)

Now both methods have the same effect as we return the result of the function and then store it in a variable that contains both values as a tuple we can reference later. Additionally I let the function save the values of sh and sw store in the Objects own variables (marked with self.variable). So you could as well reference them with x.sw and x.sh.

The function is now a method of Ddata and works on its own object (declared by (self, ... , ... , ...) for the method args. That way you can store the values inside your name scope of the class(object. Or simply return it. Your choice.

weidler
  • 676
  • 1
  • 6
  • 22
1

There is no such thing as an immutable variable in Python, and all variables can be changed in any scope you have access to.

Mutability refers to whether a variable can be mutated; for instance, you can change the contents of a list, but not a tuple. Neither of these refer to reassigning.

Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
0

When you are dealing with an immutable object, you cannot change the data within. For instance, the following is invalid because the tuple type is immutable:

somevar = (1, 2)
somevar[0] = 3  # boom!

The only way to change the data within an immutable is to create a new object with the new data and reassign it to the variable. The original object is then cleared from memory by the garbage collector if nothing points to the object anymore.

When you pass a variable to a function, if you can 'edit' it (i.e.: it is mutable), the outer-scope will see the changes because you are not changing the memory address of the variable:

def edit(l):
    l[0] = 3

somelist = [1, 2, 3]
edit(somelist)
print somelist  # [3, 2, 3]

'l' in this case is a variable that points to your global list and you are modifying the data within; you are not changing the memory address.

It is different than:

def edit(l):
    l = [3, 2, 3]

somelist = [1, 2, 3]
edit(somelist)
print somelist  # [1, 2, 3]

In this case, the memory address pointed by 'l' changes with the '=' statement. But because you are in the scope of a function, somelist still exists and still points to the old location of 'l'.

When you use 'global', you don't get a function-scoped variable like when you receive it as an argument. Instead, you are using the actual global variable, so if you reassign it, you are effectively playing with the variable at the global level and not at the function level.

somelist = [1, 2, 3]

def edit():
    global somelist
    somelist = [3, 2, 3]

edit()
print somelist  # [3, 2, 3]

-- EDIT --

In answer to you question, here's how you can change a global immutable using a function (useless example, but you can see the mechanic):

def edit(l):
    val1, val2, val3 = l
    return (3, val2, val3)

sometuple = (1, 2, 3)
sometuple = edit(sometuple)
print sometuple  # (3, 2, 3)

I suggest this read: Short Description of the Scoping Rules?

Community
  • 1
  • 1
Joe
  • 2,496
  • 1
  • 22
  • 30
  • I think this is closest to the answer I seek one question how I can do what you did with last example but without global? When I try to just change variable then nothing happens. Probably because it has same memory address how I can bypass this with normal variables? Even if I'll add return to my local example function I still get 0 output. – JeremyK Nov 07 '15 at 15:08
  • I have added an example. You need to return the new value AND assign it back when the function returns. – Joe Nov 07 '15 at 15:37