【问题标题】:Python: How to pass more than one argument to the property getter?Python:如何将多个参数传递给属性 getter?
【发布时间】:2011-08-08 14:34:30
【问题描述】:

考虑以下示例:

class A:
    @property
    def x(self): return 5

所以,当然调用a = A(); a.x 将返回5

但假设您希望能够修改属性 x。
这样,例如:

class A:
    @property
    def x(self, neg = False): return 5 if not neg else -5

并使用a = A(); a.x(neg=True) 调用它

这将引发 TypeError:'int' object is not callable,这很正常,因为我们的 x 被评估为 5

所以,如果可能的话,我想知道如何将多个参数传递给属性 getter。

【问题讨论】:

    标签: python properties language-features


    【解决方案1】:

    我认为你没有完全理解属性的用途。

    如果您创建属性x,您将使用obj.x 而不是obj.x() 访问它。 创建属性后,直接调用底层函数并不容易。

    如果您想传递参数,请将您的方法命名为 get_x 并且不要将其设为属性:

    def get_x(self, neg=False):
        return 5 if not neg else -5
    

    如果你想创建一个setter,这样做:

    class A:
        @property
        def x(self): return 5
    
        @x.setter
        def x(self, value): self._x = value
    

    【讨论】:

    • 我知道!您可以在我的第一个示例中看到 a.x 调用。
    • @Rizo:属性不是为此而设计的。
    【解决方案2】:

    在第二个示例中,您使用 a.x() 就好像它是一个函数:a.x(neg=True)。考虑到这一点,为什么不把它定义为一个函数呢?

    【讨论】:

    • 这是一个直接的解决方案,但我正在使用非常复杂的 getter 和 setter,并希望继续使用它们,而不必为属性编写单独的函数。
    • 这不能回答我的问题。我现在可以在 python 中使用函数了! :) 我只是想知道是否有某种方法可以强制属性充当函数。
    • @Rizo 为什么?如果您希望它像函数一样工作,请使用函数(方法)。但是在回答您的问题时,唯一的方法是从带有参数的属性中返回一个可调用对象。然后你会得到一些看起来完全像函数但效率较低的东西。
    • 如果 getter 除了返回一个简单的值(或默认值)之外,它还不是“getter”,而是一个常规函数。
    • @Rizo:在您给出的示例中,让 调用者 决定如何处理返回值可能更明智。毕竟是 caller 传入条件。
    【解决方案3】:

    一个属性应该只依赖于相关的对象。如果你想使用一些外部参数,你应该使用方法。

    【讨论】:

    • 假设你想在一些密码验证后访问一个属性,如果不将该密码传递给 getter,那将如何。
    • @pentanol 我认为属性方法不是 API 身份验证代码的位置。
    【解决方案4】:

    请注意,您没有property 用作装饰器。您可以非常高兴地使用旧方式并公开除属性之外的各个方法:

    class A:
        def get_x(self, neg=False):
            return -5 if neg else 5
        x = property(get_x)
    
    >>> a = A()
    >>> a.x
    5
    >>> a.get_x()
    5
    >>> a.get_x(True)
    -5
    

    这可能是一个好主意,也可能不是一个好主意,具体取决于你正在做什么(但如果我在我正在审查的任何代码中遇到这种模式,我希望在评论中看到一个很好的理由)

    【讨论】:

    • 伙计……这实际上是一个绝妙的主意。希望更多的 API 使用它而不是仅仅使用 setters-getters / only properties。
    • 更多装饰器?它们都支持以这种方式使用,因为装饰器语法只是函数调用和名称重新绑定的糖。
    • 哦,我明白你的意思了 - 更多 API 公开了 setter/getter 方法相关属性。
    • 顺便说一句,如果你使用属性装饰器,理论上你甚至不允许有参数。如果失败,您将收到诸如 <...> object is not callable 之类的错误
    • 这里没有封装的思想。以这种方式使用 getter 是没有意义的。那里的界面坏了。
    【解决方案5】:

    我知道这个问题很老,但作为参考,您可以使用如下参数调用您的属性:

    a = A()
    assert a.x == 5
    assert A.x.fget(a, True) == -5
    

    正如其他人所说,不建议这样做。

    【讨论】:

      【解决方案6】:

      在这种特殊情况下,您可以定义两个属性,它们调用底层函数:

      class A:
          @property
          def x(self):
              return self._x(neg = False)
      
          @property
          def x_neg(self):
              return self._x(neg = True)
      
          def _x(self, neg):
              return 5 if not neg else -5
      

      【讨论】:

        【解决方案7】:

        我刚刚遇到了这个问题。我有类 Polynomial() 并且我正在定义一个梯度,如果没有参数,我想返回一个函数或评估是否有参数。我想将渐变存储为一个属性,所以我不需要每次需要使用它时都计算它,并且我想使用@property,所​​以渐变属性是惰性计算的。我的解决方案是使用定义的调用方法定义一个类 Gradient 以返回多项式的 grad 属性。

        @property
        def grad(self):
            """
            returns gradient vector
            """
            class Gradient(list):
                def __call__(self, *args, **kwargs):
                    res = []
                    for partial_derivative in g:
                        res.append(partial_derivative(*args, **kwargs))
                    return res
            g = Gradient()
            for i in range(1, len(self.term_matrix[0])):
                g.append(self.derivative(self.term_matrix[0][i]))
            return g
        

        然后我成功通过了以下测试:

        def test_gradient(self):
            f = Polynomial('x^2y + y^3 + xy^3')
            self.assertEqual(f.grad, [Polynomial('2xy + y^3'), Polynomial('x^2 + 3xy^2 + 3y^2')])
            self.assertEqual(f.grad(x=1, y=2), [12, 25])
            f = Polynomial('x^2')
            self.assertEqual(f.grad(1), [2])
        

        所以,对于这个问题,我们可以尝试:

        class A:
            @property
            def x(self):
                class ReturnClass(int):
                    def __call__(self, neg=False):
                        if not neg:
                            return 5
                        return -5
                return ReturnClass()
        

        【讨论】:

        • 不确定该解决方案是否有效:如果我说a = A(); print(a.x),结果是 0 - 未初始化的 int 的默认值。解决方案 - 据了解 - 应该是 5 for a.x
        猜你喜欢
        • 2019-02-11
        • 2019-07-04
        • 1970-01-01
        • 1970-01-01
        • 2020-07-22
        • 1970-01-01
        • 2020-08-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多