简体   繁体   English

如何使变量(真正)局部于过程或函数

[英]How to make a variable (truly) local to a procedure or function

ie we have the global declaration, but no local. 即我们有全局声明,但没有局部声明。

"Normally" arguments are local, I think, or they certainly behave that way. 我认为“通常”的论点是局部的,或者肯定是那样的。 However if an argument is, say, a list and a method is applied which modifies the list, some surprising (to me) results can ensue. 但是,如果某个参数是一个列表,并且应用了一种修改列表的方法,那么可能会产生一些令人惊讶的结果(对我而言)。

I have 2 questions: what is the proper way to ensure that a variable is truly local? 我有两个问题:确保变量真正是局部变量的正确方法是什么? I wound up using the following, which works, but it can hardly be the proper way of doing it: 我使用了以下可行的方法来结束工作,但这几乎不是正确的方法:

 def AexclB(a,b):
    z = a+[]   # yuk
    for k in range(0, len(b)):
        try:    z.remove(b[k])
        except: continue
    return z

Absent the +[], "a" in the calling scope gets modified, which is not desired. 如果没有+ [],则调用范围中的“ a”会被修改,这是不希望的。 (The issue here is using a list method, (这里的问题是使用列表方法,

The supplementary question is, why is there no "local" declaration? 补充的问题是,为什么没有“本地”声明?

Finally, in trying to pin this down, I made various mickey mouse functions which all behaved as expected except the last one: 最后,在尝试解决此问题时,我做了各种米老鼠功能,除了最后一个功能外,其他功能均与预期的一样:

def fun4(a):
   z = a
   z = z.append(["!!"])
   return z

a = ["hello"]
print "a=",a
print  "fun4(a)=",fun4(a)
print "a=",a

which produced the following on the console: 在控制台上产生了以下内容:

a= ['hello']
fun4(a)= None
a= ['hello', ['!!']]
...
>>>

The 'None' result was not expected (by me). (我)没料到“无”结果。

Python 2.7 btw in case that matters. Python 2.7 btw,以防万一。

PS: I've tried searching here and elsewhere but not succeeded in finding anything corresponding exactly - there's lots about making variables global, sadly. PS:我曾尝试在这里和其他地方进行搜索,但未成功找到完全对应的任何内容-遗憾的是,有很多关于使变量全局化的事情。

It's not that z isn't a local variable in your function. 不是z不是函数中的局部变量。 Rather when you have the line z = a , you are making z refer to the same list in memory that a already points to. 相反,当你有行z = a ,你正在z指向同一个列表内存中a已指向。 If you want z to be a copy of a , then you should write z = a[:] or z = list(a) . 如果你想z是副本a ,那么你应该写z = a[:]z = list(a)

See this link for some illustrations and a bit more explanation http://henry.precheur.org/python/copy_list 请参阅此链接获取一些插图和更多说明http://henry.precheur.org/python/copy_list

Python will not copy objects unless you explicitly ask it to. 除非您明确要求,否则Python不会复制对象。 Integers and strings are not modifiable, so every operation on them returns a new instance of the type. 整数和字符串不可修改,因此对它们的每个操作都会返回该类型的新实例。 Lists, dictionaries, and basically every other object in Python are mutable, so operations like list.append happen in-place (and therefore return None ). 列表,字典以及Python 基本上所有其他对象都是可变的,因此list.append操作就地进行(因此返回None )。

If you want the variable to be a copy, you must explicitly copy it. 如果希望变量为副本,则必须显式复制它。 In the case of lists, you slice them: 对于列表,可以将它们切片:

z = a[:]

What you're missing is that all variable assignment in python is by reference (or by pointer, if you like). 您所缺少的是python中的所有变量赋值都是通过引用(如果需要,也可以通过指针)进行的。 Passing arguments to a function literally assigns values from the caller to the arguments of the function, by reference. 通过引用将参数传递给函数实际上是将调用者的值分配给函数的参数。 If you dig into the reference, and change something inside it, the caller will see that change. 如果您深入参考,并其中进行了更改,则调用者将看到该更改。

If you want to ensure that callers will not have their values changed, you can either try to use immutable values more often (tuple, frozenset, str, int, bool, NoneType), or be certain to take copies of your data before mutating it in place. 如果要确保调用者不会更改其值,则可以尝试更频繁地使用不可变值(元组,frozenset,str,int,bool,NoneType),或者一定要在更改数据之前对其进行复制到位。

In summary, scoping isn't involved in your problem here. 总之,这里的范围不涉及您的问题。 Mutability is. 可变性是。

Is that clear now? 现在清楚了吗?


Still not sure whats the 'correct' way to force the copy, there are various suggestions here. 仍然不确定强制复制的“正确”方法是什么,这里有各种建议。

It differs by data type, but generally <type>(obj) will do the trick. 它因数据类型而异,但是通常<type>(obj)可以解决问题。 For example list([1, 2]) and dict({1:2}) both return (shallow!) copies of their argument. 例如list([1, 2])dict({1:2})都返回(浅!)其参数的副本。

If, however, you have a tree of mutable objects and also you don't know a-priori which level of the tree you might modify, you need the copy module. 但是,如果您有一棵可变对象 ,并且您不知道您可能会修改树的哪个级别,则需要copy模块。 That said, I've only needed this a handful of times (in 8 years of full-time python), and most of those ended up causing bugs. 就是说,我只需要很少的时间(在8年的全职python中),大多数最终导致bug。 If you need this, it's a code smell, in my opinion. 我认为,如果您需要此功能,那是一种代码味道。

The complexity of maintaining copies of mutable objects is the reason why there is a growing trend of using immutable objects by default. 维护可变对象副本的复杂性是默认情况下使用不可变对象的趋势不断增长的原因。 In the clojure language, all data types are immutable by default and mutability is treated as a special cases to be minimized. 在clojure语言中,默认情况下所有数据类型都是不可变的,而可变性被视为一种特殊情况,应尽量减少这种情况。

There is a great answer than will cover most of your question in here which explains mutable and immutable types and how they are kept in memory and how they are referenced. 有一个很好的答案比这里涵盖了您的大部分问题,它解释了可变和不可变的类型以及它们如何保存在内存中以及如何被引用。 First section of the answer is for you. 答案的第一部分适合您。 (Before How do we get around this? header) (在如何解决此问题之前?标题)

In the following line 在下一行

z = z.append(["!!"])

Lists are mutable objects, so when you call append , it will update referenced object, it will not create a new one and return it. 列表是可变对象,因此,当您调用append ,它将更新引用的对象,不会创建新的对象并返回它。 If a method or function do not retun anything, it means it returns None . 如果某个方法或函数不返回任何内容,则表示它返回None

Above link also gives an immutable examle so you can see the real difference. 上面的链接还提供了一个不变的示例,因此您可以看到真正的区别。

You can not make a mutable object act like it is immutable. 您不能使可变对象表现为不可变的。 But you can create a new one instead of passing the reference when you create a new object from an existing mutable one. 但是,当您从现有可变对象创建新对象时,可以创建一个新对象而不是传递引用。

a = [1,2,3]
b = a[:]

For more options you can check here 有关更多选项,请点击此处

If you need to work on a list or other object in a truly local context you need to explicitly make a copy or a deep copy of it. 如果需要在真正的本地上下文中处理列表或其他对象,则需要显式地对其进行复制或深层复制。

from copy import copy
def fn(x):
    y = copy(x)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM