【问题标题】:Change object directly in function is anti-pattern in python?直接在函数中更改对象是python中的反模式吗?
【发布时间】:2020-02-20 08:26:26
【问题描述】:

假设我们必须得到一些值并从函数中改变它。

方式 1

def change_b(obj):
    obj['b'] = 4


result = {'a': 1, 'b': 2}
change_b(obj=result)
print(result)

如您所知,函数change_b() 直接在函数中更改result['b'] 的值。

方式 2

from copy import deepcopy


def change_b(obj):
    temp = deepcopy(obj)
    temp['b'] = 4
    return temp


result = {'a': 1, 'b': 2}
result = change_b(obj=result)
print(result)

但是Way-2将对象复制到新对象并替换新对象的值。

所以,原始对象不会影响任何东西。 (而且,没有副作用)

也许Way-2更安全,因为它不会改变原始对象。

我想知道哪种方式更通用和pythonic?

谢谢。

【问题讨论】:

  • 为什么要做深拷贝而不是浅拷贝?
  • @wjandrea 这只是一个例子。深拷贝或浅拷贝都不重要。
  • pandas 中的一些函数可以双向工作。他们创建新数据,或者如果您添加 inplace=True,他们可以更新现有数据。这是个好方法。
  • 两种方式都可以。最糟糕的是当函数没有获取参数但使用global 更改数据时。

标签: python api-design in-place mutability


【解决方案1】:

根据我对 Python 的理解,解决这个问题的最 Pythonic 的方法是非常清楚正在发生的事情。只要你这样做,我认为这并不重要。

my_dict = {'a': 3, 'b': 4}
double_values_in_dict(my_dict)

# Some other code

这是一个人为的例子,但很清楚这里的意图是什么,即使没有包含方法定义。 不清楚的是,如果您将 double_values_in_dict 的返回值分配给一个新变量;那时我不知道您可能对原始 dict 对象做了什么,我必须开始深入研究该方法以弄清楚它实际上是如何工作的。

【讨论】:

    【解决方案2】:

    “显式优于隐式”
    ...
    “面对歧义,拒绝猜测的诱惑。”

    -PEP 20


    在函数中修改参数不一定是坏事。 不好的是没有充分理由这样做。如果您对函数名称和文档很清楚,参数将在函数中进行修改,那很好。如果函数修改了参数而没有任何迹象表明它正在尝试这样做,那就不太好。

    在这种情况下,您的 Way-1 更简单、更明确。很明显,变量要被改变,而改变的方式可以很容易地通过查看代码来确定。

    Way-2 更糟糕,因为名称change_b 暗示该参数将被修改,而它不是。在不修改原始参数的情况下返回参数的修改版本是 python 中的标准设计模式,但最好明确说明。

    例如python内置的set数据结构有对应的方法:set.difference(other)set.difference_update(other)。在这两种情况下,它们都做同样的事情:计算这个集合和给定集合之间的差异。在前一种情况下,返回结果而不修改原始集。在后一种情况下,原始集合被修改并且不返回任何内容。弄清楚哪个做什么是非常简单的。

    一般来说,您应该避免更新一个值返回相同的值,因为这更加模棱两可。请注意大多数 python 方法是如何做其中一个或另一个,但不是两者都做(以及那些同时做的,比如list.pop(),这样做是明智的,返回的对象不是被修改的对象)。

    【讨论】:

      【解决方案3】:

      总结

      如果 API 明确表示它正在更新其输入,则 Way-1 很好且可取:add_route(route_map, new_route)

      如果 API 主要用于做其他事情,那么 Way-2 可以避免意外的副作用。

      Python 中的示例

      Way-1dict.update()list.sort() 进行就地更新,因为这是它们的主要功能工作。

      Way-2:内置 sorted() 函数从其输入中生成一个新的排序列表,并注意不要更改该列表。大致来说,它是这样做的:

      def sorted(iterable, *, key=None, reverse=False):
          result = list(iterable)                # copy the data
          result.sort(key=key, reverse=reverse)  # in-place sort
          return result
      

      希望阐明何时复制以及何时就地变异:-)

      【讨论】:

      • 也就是说,没有通用的办法,因为要视情况而定?
      • @Hide:是的,这是真的。但是,我认为这是一条单一的规则,“API 应该专注于它们的主要任务,而不是有附带的副作用”。
      猜你喜欢
      • 2014-10-07
      • 2016-10-09
      • 1970-01-01
      • 1970-01-01
      • 2017-06-09
      • 2014-09-02
      • 2014-05-27
      • 2020-04-23
      • 1970-01-01
      相关资源
      最近更新 更多