Python:如何通过引用传递字符串?

38
从这个链接:如何通过引用传递变量?,我们知道,当将字符串(一个不可变类型变量)作为参数传递给函数时,Python会复制它,但如果字符串很大,这将浪费内存。在许多情况下,我们需要使用函数来封装一些字符串操作,因此我想知道如何更有效地实现?

我不确定你在这里问什么...你有一些代码可以展示你遇到的问题吗? - g.d.d.c
1
我们知道,当将一个字符串(一个不可变类型的变量)作为参数传递给函数时,Python会复制它。你绝对不知道这个!Python从不隐式地复制任何东西。 - Mike Graham
7个回答

49

Python函数中传递的对象(包括字符串)不会被复制:

>>> def foo(s):
...     return id(s)
...
>>> x = 'blah'
>>> id(x) == foo(x)
True

如果您需要在函数中“修改”字符串,请返回新字符串并将其重新赋值给原始名称:

>>> def bar(s):
...     return s + '!'
...
>>> x = 'blah'
>>> x = bar(x)
>>> x
'blah!'

不幸的是,当对大字符串进行小更改时,这种方法可能非常低效,因为大字符串会被复制。Pythonic 的处理方式是将字符串保存在列表中,一旦拥有所有段落就将它们连接起来。


1
Python从不按引用传递。它传递引用,但“按引用传递”已经被另一种参数传递样式(例如函数swap(a, b))占用了。 - user395760
1
@delnan:收到。无论你如何称呼它,当名称被分配给参数时,Python不会复制所引用的对象。 - Steven Rumbalski
1
当然可以。问题只在于术语。同样适用于其他赋值情况和其他对象,因此您可能希望概括您的答案。 - user395760
1
@delnan -- 也许我不是个典型的计算机科学家类型的人,但你经常会看到C语言中的人写一个函数swap(&a, &b)并将其称为按引用传递。然而,在Python中实际上你所做的与此非常相似。你可以将对象传入函数并在函数中更改该对象,但无法更改“指针”指向的位置。唯一的区别是Python具有不可变对象,因此对于不可变对象,你不允许更改“指针”引用的对象。无论如何,我从来不清楚应该如何称呼它(对于Python或C)。 - mgilson
抱歉,我撤回之前的评论。我没有仔细阅读OP的帖子。我应该多睡一会儿。很棒的答案(+1)。 - mgilson
1
@mgilson 我同意这是传递引用,因为它实现了相同的功能(我只关心语义)。不同之处在于,C 有指向所有东西的指针,因此您可以传递指向(例如)局部变量或结构成员的指针。在 Python 中,没有这样的东西 - 每个变量、成员、集合项等都是一个“引用”,你不能有一个“引用到引用”,所以你实际上不能做到像传递引用一样(无论是内置于语言中还是通过 C 风格指针模拟)。 - user395760

8

Python确实是通过引用传递字符串的。请注意,具有相同内容的两个字符串被认为是相同的:

a = 'hello'
b = 'hello'
a is b        # True

当b被分配一个值时,如果该值已经存在于内存中,则b将使用该字符串的同一引用。请注意另一个事实,如果该字符串是动态创建的,即通过字符串操作(例如连接)创建的,则新变量将引用相同字符串的新实例:

c = 'hello'
d = 'he'
d += 'llo'
c is d        # False

话虽如此,创建一个新字符串将在内存中分配一个新字符串并返回一个新字符串的引用,但是使用已经创建的字符串将重复使用同一个字符串实例。因此,将字符串作为函数参数传递通过引用传递它,或者换句话说,将传递字符串在内存中的地址。

现在到你想知道的问题-如果你在函数内部更改了字符串,那么函数外的字符串将保持不变,这源于字符串的不可变性。更改字符串意味着在内存中分配一个新字符串。

a = 'a'
b = a    # b will hold a reference to string a
a += 'a'
a is b   # False

底线:

你不能真正地改变一个字符串。这对于可能所有的编程语言都是一样的(但请不要引用我)。 当你将字符串作为参数传递时,你传递的是一个引用。当你改变它的值时,你改变了变量指向内存中另一个位置的指针。但当你改变一个变量的引用时,指向相同地址的其他变量自然会保留他们原来持有的旧值(引用)。 希望这个解释足够清晰易懂。


你说得对,Java中的字符串也是不可变的。 https://stackoverflow.com/questions/22397861/why-is-string-immutable-in-java - undefined

6
In [7]: strs="abcd"

In [8]: id(strs)
Out[8]: 164698208

In [9]: def func(x):
    print id(x)
    x=x.lower() #perform some operation on string object, it returns a new object
    print id(x)
   ...:     

In [10]: func(strs)
164698208              # same as strs, i.e it actually passes the same object 
164679776              # new object is returned if we perform an operation
                       # That's why they are called immutable  

但是对字符串的操作总是返回一个新的字符串对象。


6

如果想要潜在地改变传入的某个值,请将其包装在字典或列表中:

这不会改变 s 的值。

def x(s):
  s += 1

这会改变 s:

def x(s):
  s[0] += 1

这是唯一的“按引用传递”的方法。


5
def modify_string( t ):
  the_string = t[0]
  # do stuff

modify_string( ["my very long string"] )

4
将字符串包装到一个类中,可以使其按引用传递。
    class refstr:
       "wrap string in object, so it is passed by reference rather than by value"
       def __init__(self,s=""):
          self.s=s
       def __add__(self,s):
          self.s+=s
          return self
       def __str__(self):
          return self.s

    def fn(s):
       s+=" world"

    s=refstr("hello")
    fn(s) # s gets modified because objects are passed by reference
    print(s) #returns 'hello world' 

1

只需像处理其他参数一样传递即可。内容不会被复制,只有引用会被传递。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接