如果它是全局的,为什么你可以改变不可变的

Why you can change immutable if it's global

任何人都可以向我解释为什么我可以更改不可变变量,如果它在 Python 中是全局变量吗?我不明白为什么这在全局状态下是允许的,而在其他任何地方都不允许。我最近开始学习编程,但我就是不明白。示例如下。

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

Python 中没有不可变变量这回事,所有变量都可以在您有权访问的任何范围内更改。

可变性是指一个变量是否可以被改变;例如,您可以更改列表的内容,但不能更改元组。这些都不是指重新分配。

我认为您混淆了同名的全局变量和函数局部变量。
考虑例如

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()

这是因为在 python 中,当任何值分配给尚不可用的变量时,都会创建一个新变量。
为避免创建新变量并更新全局变量,您必须明确提及使用关键字 global

一些核心的东西:

  • 根据变量的作用命名您的变量,其他人无法阅读您的代码
  • 不要使用 global。它从来都不是必需的,根本不是好的做法,并且会让您很难概览您的代码。我知道你这样做是为了展示一些东西,但仍然只是提示 ;)

  • 编辑: 如果不清楚:实例变量(objects/classes 的变量)不是不可变的;)

关于不可变对象的事情:

不变性的概念不是您在这里看到的。不可变变量是指创建后无法更改的变量。一个例子是元组,因为您之后无法更改它们的组件。

您在这里看到的只是名称范围。

如果您在函数内更改变量,它会在它们自己的本地名称范围内更改它们。虽然他们不会改变给他们的东西,因为这会产生很大的问题(考虑一个变量,比如给函数的名字,如果是名字,它应该给你第一个字母。如果函数会自动改变名字,程序会 "forget" 它和后面只有第一个字母)。 如果你想在函数之外使用函数的结果 return 它们。如果你想使用 classes 的概念,其中函数(对于 classes 它们被称为方法)改变对象(class 的)的属性(变量),那么你有将这些函数声明为 class 的方法。 在您的示例中,您可以这样称呼它们:x.sp(...).

有关 类(面向对象编程)概念的更多信息,请在网上搜索大量很好的解释和示例。它是编程的核心概念,学习起来非常有用!

编辑: 由于这似乎不太清楚,我为两种可能性编辑了您的代码:

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)

现在这两种方法都具有相同的效果,因为我们 return 函数的结果,然后将其存储在一个包含两个值的变量中作为我们稍后可以引用的元组。此外,我让函数将 sh 和 sw 的值保存在对象自己的变量中(标记为 self.variable)。所以你也可以用 x.sw 和 x.sh.

来引用它们

该函数现在是 Ddata 的一个方法,并在其自己的对象(由 (self, ... , ... , ...) 声明的方法参数)上工作。这样您就可以将值存储在内部你的 class( 对象的名称范围。或者只是 return 它。你的选择。

当您处理不可变对象时,您无法更改其中的数据。例如,以下是无效的,因为元组类型是不可变的:

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

更改不可变数据的唯一方法是使用新数据创建一个新对象并将其重新分配给变量。如果没有任何对象再指向该对象,则垃圾收集器将从内存中清除原始对象。

当你将一个变量传递给一个函数时,如果你可以 'edit' 它(即:它是可变的),外部范围将看到变化,因为你没有改变变量的内存地址:

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

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

'l' 在这种情况下是一个指向全局列表的变量,您正在修改其中的数据;你没有改变内存地址。

不同于:

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

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

在这种情况下,'l'指向的内存地址随着'='语句的变化而变化。但是因为你在一个函数的范围内,所以somelist仍然存在并且仍然指向'l'.

的旧位置

当您使用 'global' 时,您不会像将其作为参数接收时那样获得函数范围的变量。相反,您使用的是实际的全局变量,因此如果您重新分配它,您实际上是在全局级别而不是函数级别使用该变量。

somelist = [1, 2, 3]

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

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

-- 编辑--

在回答你的问题时,这里是你如何使用一个函数来改变一个全局不可变的(无用的例子,但你可以看到机制):

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

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

我建议阅读:Short Description of the Scoping Rules?