【问题标题】:Pass list to function by value按值传递列表以发挥作用
【发布时间】:2013-02-28 21:36:03
【问题描述】:

我想按值将列表传递给函数。 默认情况下,列表和其他复杂对象通过引用传递给函数。 这是一个愿望:

def add_at_rank(ad, rank):
    result_ = copy.copy(ad)
    .. do something with result_
    return result_

这可以写得更短吗? 换句话说,我不想改变ad

【问题讨论】:

  • 如果ad是一个列表,你可以做result_ = ad[:]
  • 它真的不能写得更短,不。您必须明确复制列表。您可以在函数外部或函数内部执行此操作,但您必须这样做。
  • 其实引用是按值传递的
  • Python 中不经常使用术语“按值”和“按引用”,因为它们往往会误导来自其他语言的人。正如@gnibbler 所说,Python 中的所有内容都是按值传递的——但有些值是引用。 :)
  • 另外,通常的 Pythonic 处理方法是首先避免改变 result_;通过转换旧的list(例如,使用列表理解、mapfilter 等)创建一个新的list。有时这是不合适的,而且在不知道您的“.. do something with result_”代码看起来或做了什么的情况下,不可能在这里说这是否正确。但它比 Python 新手(尤其是 Java 或 C++ 等语言的新手)更适合。

标签: python pass-by-value


【解决方案1】:

您可以使用[:],但对于包含列表(或其他可变对象)的列表,您应该使用copy.deepcopy()

lis[:] 等价于list(lis)copy.copy(lis),并返回列表的浅表副本。

In [33]: def func(lis):
    print id(lis)
   ....:     

In [34]: lis = [1,2,3]

In [35]: id(lis)
Out[35]: 158354604

In [36]: func(lis[:])
158065836

何时使用deepcopy()

In [41]: lis = [range(3), list('abc')]

In [42]: id(lis)
Out[42]: 158066124

In [44]: lis1=lis[:]

In [45]: id(lis1)
Out[45]: 158499244  # different than lis, but the inner lists are still same

In [46]: [id(x) for x in lis1] = =[id(y) for y in lis]
Out[46]: True

In [47]: lis2 = copy.deepcopy(lis)  

In [48]: [id(x) for x in lis2] == [id(y) for y in lis]  
Out[48]: False

【讨论】:

    【解决方案2】:

    这可能是装饰器功能的一个有趣用例。像这样的:

    def pass_by_value(f):
        def _f(*args, **kwargs):
            args_copied = copy.deepcopy(args)
            kwargs_copied = copy.deepcopy(kwargs)
            return f(*args_copied, **kwargs_copied)
        return _f
    

    pass_by_value 将函数 f 作为输入并创建一个新函数 _f,该函数深度复制其所有参数,然后将它们传递给原始函数 f

    用法:

    @pass_by_value
    def add_at_rank(ad, rank):
        ad.append(4)
        rank[3] = "bar"
        print "inside function", ad, rank
    
    a, r = [1,2,3], {1: "foo"}
    add_at_rank(a, r)
    print "outside function", a, r
    

    输出:

    "inside function [1, 2, 3, 4] {1: 'foo', 3: 'bar'}"
    "outside function [1, 2, 3] {1: 'foo'}"
    

    【讨论】:

    • 不错的一个。另外,最好用@functools.wraps(f)装饰你的内心_f
    【解决方案3】:

    浅拷贝通常足够好,并且可能比深拷贝更快。

    如果您对result_ 所做的修改没有改变它包含的项目/属性,您可以利用这一点。

    举个简单的例子,如果你有一个棋盘

    board = [[' ']*8 for x in range(8)]
    

    你可以做一个浅拷贝

    board2 = copy.copy(board)
    

    追加/插入/弹出/删除/替换来自board2 的项目是安全的,但它包含的列表不安全。如果您想修改其中一个包含的列表,您必须创建一个新列表和replace 现有列表

    row = list(board2[2])
    row[3] = 'K'
    board2[2] = row
    

    工作量多一点,但时间和存储效率要高得多

    【讨论】:

      【解决方案4】:

      如果 adlist,您可以简单地将您的函数称为 add_at_rank(ad + [], rank)

      这将在您每次调用函数时创建 list 的新实例,该值相当于 ad

      >>>ad == ad + []
      True
      
      >>>ad is ad +[]
      False
      

      纯pythonic :)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-11-28
        • 2020-09-21
        • 2020-12-15
        • 2020-06-21
        • 2021-11-17
        相关资源
        最近更新 更多