0

我读过,在编写函数时,最好将参数复制到其他变量中,因为变量是否不可变并不总是很清楚。[我不记得在哪里,所以不要问]。我一直在根据这个编写函数。

据我了解,创建一个新变量需要一些开销。它可能很小,但它就在那里。那么应该怎么做呢?我应该创建新变量还是不保存参数?

我读过这个这个。如果可以轻松更改 float 和 int 为什么它们是不可变的,我对此感到困惑?

编辑:

我正在编写简单的函数。我将发布示例。在我读到 Python 中的参数应该被复制后,我写了第一个,而在我通过反复试验意识到不需要它之后,我写了第二个。

#When I copied arguments into another variable
def zeros_in_fact(num):
    '''Returns the number of zeros at the end of factorial of num'''
    temp = num
    if temp < 0:
        return 0
    fives = 0
    while temp:
        temp /=  5
        fives += temp
    return fives

#When I did not copy arguments into another variable
def zeros_in_fact(num):
    '''Returns the number of zeros at the end of factorial of num'''
    if num < 0:
        return 0
    fives = 0
    while num:
        num /=  5
        fives += num
    return fives
4

3 回答 3

3

我认为最好在此类问题中保持简单。

您问题中的第二个链接是一个非常好的解释;总之:

方法采用参数,正如该解释中所指出的,这些参数是“按值”传递的。函数中的参数采用传入的变量的值。

对于字符串、整数和浮点数等基本类型,变量的值是指向表示数字或字符串的内存空间的指针(下图中的箭头)。

code               | memory
                   |
an_int = 1         |    an_int ---->  1
                   |                  ^    
another_int = 1    |    another_int  /

当您在方法中重新分配时,您会更改箭头指向的位置。

an_int = 2         |    an_int -------> 2
                   |    another_int --> 1

数字本身不会改变,并且由于这些变量仅在函数内部和函数外部具有作用域,因此传入的变量与之前保持相同:1 和 1。但是当您传入列表或对象时,对于例如,您可以更改它们指向函数外部的值

a_list = [1, 2, 3]    |              1   2   3
                      |   a_list ->| ^ | ^ | ^ |
                      |              0   2   3
a_list[0] = 0         |   a_list ->| ^ | ^ | ^ |

现在,您可以更改列表或对象中的箭头指向的位置,但列表的指针仍指向与以前相同的列表。(上图中的两组箭头实际上应该只有一个 2 和 3,但箭头会变得难以绘制。)

那么实际的代码是什么样的呢?

a = 5
def not_change(a):
  a = 6
not_change(a)
print(a) # a is still 5 outside the function

b = [1, 2, 3]
def change(b):
  b[0] = 0
print(b) # b is now [0, 2, 3] outside the function

是否复制给定的列表和对象(整数和字符串无关紧要)并因此返回新变量或更改传入的变量取决于您需要提供的功能。

于 2013-06-18T06:18:39.360 回答
2

您在代码示例中所做的工作没有明显的开销,但它也没有完成任何事情,因为它不会保护您免受可变/不可变问题的影响。

考虑这一点的方法是 Python 中有两种东西:名称和对象。当您这样做时,x = y您正在对名称进行操作,将该名称附加到 object y。当您这样做x += y或其他增强的赋值运算符时,您还绑定了一个名称(在这种情况下,除了执行您使用的操作+)。您所做的任何其他事情都是对对象进行操作。如果对象是可变的,则可能涉及更改它们的状态。

整数和浮点数不能更改。您可以做的是更改名称所指的 int 或 float 。如果你这样做

x = 3
x = x + 4

你没有改变int。您正在更改名称x,以便它现在附加到数字 7 而不是数字 3。另一方面,当您这样做时:

x = []
x.append(2)

您正在更改列表,而不仅仅是将名称指向新对象。

当您对同一对象有多个名称时,可以看出差异。

>>> x = 2
>>> y = x
>>> x = x + 3 # changing the name
>>> print x
5
>>> print y # y is not affected
2
>>> x = []
>>> y = x
>>> x.append(2) # changing the object
>>> print x
[2]
>>> print y # y is affected
[2]

改变一个对象意味着你改变了对象本身,以便所有指向它的名称都能看到变化。如果您只是更改一个名称,其他名称不会受到影响。

您链接到的第二个问题提供了有关它在函数参数上下文中如何工作的更多信息。增强的赋值运算符(+=,*=等)有点棘手,因为它们对名称进行操作,但也可能同时改变对象。您可以在 StackOverflow 上找到有关其工作原理的其他问题。

于 2013-06-18T06:28:53.480 回答
1

如果您要重新绑定名称,则它包含的对象的可变性是无关紧要的。只有当您执行变异操作时,您才必须创建一个副本。(如果你在字里行间读到,那间接表示“不要改变传递给你的对象”。)

于 2013-06-18T06:16:07.647 回答