【问题标题】:Python - Chaining methods: returning `self` vs returning a new cloned objectPython - 链接方法:返回`self`与返回一个新的克隆对象
【发布时间】:2013-06-03 17:11:01
【问题描述】:

由于我正在处理的项目中存在一些限制,我不得不将 Django 的 QuerySet 类替换为自定义类。 QuerySet 对象可以将它们的方法链接起来(例如QuerySet().filter(...).exclude(...) 等等),所以在我的实现中,每个方法都简单地返回self。所以我的班级看起来像这样:

class MyQuerySet:
    ...
    def filter(self, *args, **kwargs):
        # Do some stuff and then:
        return self

这样我模仿了 Django 的 QuerySet 行为。

然而,查看 Django 代码,我注意到 QuerySet 的方法每次调用时都返回一个克隆对象,而不是返回 self。它看起来像这样(删除了不必要的东西):

class QuerySet(...):
    ...
    def filter(self, *args, **kwargs):
        clone = self._clone()
        # Do some stuff and then
        return clone

    def _clone(self,...):
        klass = self.__class__
        obj = klass(...)
        return obj

所以基本上,每次调用方法时,QuerySet 都会克隆自己,实例化一个新对象并返回它。

我的问题是:为什么?我的方法不对吗?
我担心我这样做的方式可能会中断,否则我无法解释为什么 Django 团队会这样做。

【问题讨论】:

  • 返回一个克隆的对象意味着原始对象是保证不变的(你正在更改克隆,而不是原始对象)。不过,这只是我的一个观察。
  • @RobertHarvey - 看起来很有效,看看这里的解释:docs.djangoproject.com/en/1.0/ref/models/querysets/#all
  • 我正在查看该文件的git blame 输出,我看到self._clone() 来自多个提交。不管是什么原因,这种设计已经持续实施了一段时间。 github.com/django/django/blame/master/django/db/models/query.py
  • +1 非常有趣的问题。
  • @AdriánLópez,不幸的是没有。由于架构要求,我们必须将数据库移到“墙”后面,并通过 TCP/IP 代理与其通信。因此,我们将发送请求,而不是查询。这超出了 Django 的范围。

标签: python django django-queryset


【解决方案1】:

Django 这样做是为了保留和重用基本查询,而无需从未来的“子”查询继承更改,例如 exclude() 上的 filter()。我猜有人尝试存储查询以供以后使用,并意识到如果不复制就无法正常工作。

我克隆了 django 存储库并在 django/db/models/query.py 上快速搜索了 git log,搜索短语 clone。

引入此更改的补丁在这里:

https://github.com/django/django/commit/d4a3a4b

【讨论】:

  • 谢谢@jpaugh!看起来你已经确定了这个方法的初始使用。
猜你喜欢
  • 1970-01-01
  • 2012-05-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多