【问题标题】:Python setter to also return a valuePython setter 也返回一个值
【发布时间】:2013-05-13 00:35:24
【问题描述】:

我想知道是否有可能让一个属性设置器也返回一个值。下面的代码试图从表面上解释问题。

基本上我需要为一个对象设置一个属性,但在我需要检查它是否唯一之前。我想知道是否有办法直接将唯一名称返回给用户,而无需事后查询对象的新名称。

class MyClass(object):
    def __init__(self,ID):
        self._ID = ID
    @property
    def Name(self):
        return DBGetName(self._ID)

    @Name.setter
    def Name(self, value):
        UniqueName = DBGetUniqueName(self._ID,value)
        return DBSetName(UniqueName)

Myinstance = MyClass(SomeNumber)
#What I do now
Myinstance.Name = "NewName"
Uniquename = Myinstance.Name

#What I was wondering if possible. Line below is syntactically invalid, but shows the idea.
Name = (Myinstance.Name = "NewName")

编辑: 这是一个伪代码,我忘记将值实际传递给内部函数。我的错。

【问题讨论】:

  • 我对此有点困惑。看起来 setter 完全忽略了您传递给它的值。一个接受 MyInstance.Name = "NewName" 并将名称设置为 "NewName-071839" 的设置器我可以理解,但这会让我感到困惑(即使在 JavaScript 或 C# 或其他允许它的语言中)。
  • 好吧,Name = Myinstance.Name = "NewName" 在语法上是有效的,但是 setter 的返回值将被忽略,它只会将 Name 设置为 "NewName"
  • @Aya:好点。如果没有括号,这将是一个链式赋值语句,这意味着“将此值分配给所有链式在一起的名称”。在非常简单的情况下,这与 C/JS/etc 的作用相同。 “将此值分配给姓氏,然后将该分配的结果分配给它之前的那个,......”,但在这种情况下它没有帮助。
  • @abarnert 当然。不过,如果您有 C++ 背景,并且使用过operator=,您可能希望能够在 Python 中做类似的事情。 TBH,我很高兴您不能在 Python 中使用赋值作为表达式 - 我有时仍将 if foo == 1 错误输入为 if foo = 1,尤其是当我在 Python 和 SQL 编码之间切换时,Python 发现这很好对我来说那个特殊的错误,因为它很容易被错过。
  • @Aya:另一个好点。链式赋值在两个可能让新手混淆的方式表面上类似于 C++,而不仅仅是 C 和 JS 的一种方式……无论如何,我也很高兴 Python 不允许赋值表达式,但它更多的是成为一整套功能的一部分,这些功能使几乎所有 Python 代码的结构都比几乎任何 JS 或 C++ 代码更具可读性。

标签: python python-3.x decorator setter getter-setter


【解决方案1】:

使用 Python3.8 或更高版本:

class MyClass(object):
    def __init__(self,ID):
        self._ID = ID
    @property
    def Name(self):
        return DBGetName(self._ID)

    @Name.setter
    def Name(self, value):
        UniqueName = DBGetUniqueName(self._ID,value)
        return DBSetName(UniqueName)

Myinstance = MyClass(SomeNumber)

Myinstance.Name = (Uniquename := Myinstance.Name) # Python >= 3.8

【讨论】:

    【解决方案2】:

    我从一个类似的问题开始,但我只需要从 setter 获取“通用”信息即可自动创建 GUI; GUI 代码查看属性列表,找到具有设置器的属性,然后创建输入字段。然而,有些输入是字符串,有些是浮点数等,并且 GUI 代码会生成适当的输入字段。我知道这是一个杂项,但我将这些信息放在 getter 的文档字符串中。

    我知道这不是您问题的答案,但可能会帮助看起来像我的人!

    class Dog():
        def __init__(self):
            self._name = None
            return
        @property
        def name(self):
            """Doggos are the best!"""
            return self._name
        @name.setter
        def name(self, n):
            self._name = n
            return
    def main():
        terrier = Dog()
        terrier.name = "Rover"
        print("Dog's name is ", terrier.name)
        print(type(terrier).name.__doc__)
        # also works with...
        print(getattr(type(terrier), 'name').__doc__)
        return
    if __name__ == '__main__':
        main()
    

    【讨论】:

      【解决方案3】:

      setter 当然可以返回一个值。

      但这样做并不是很有用,因为 setter 通常用于赋值语句中——就像在你的示例中一样。

      问题在于,在 Python 中,赋值不是表达式,而是语句。它没有可以使用的值,也不能将其嵌入到另一个语句中。所以,没有办法写出你想写的那一行。

      您可以改为显式调用设置器……但在这种情况下,将其编写为常规方法而不是属性设置器会更清晰。


      就您而言,我认为常规方法正是您想要的。您完全忽略了传递给 setter 的值。这会让你的代码的读者感到困惑(包括 6 个月后的你)。它根本不是一个真正的 setter,它是一个创建唯一名称的函数。那么,为什么不这样称呼它呢?

      def CreateUniqueName(self):
          UniqueName = DBGetUniqueName(self._ID)
          return DBSetName(UniqueName)
      

      (值得注意的是,DBSetName 返回其参数本身并不是很 Pythonic……)


      如果您想知道为什么Python 以这种方式工作,请参阅官方常见问题解答中的Why can't I use an assignment in an expression?

      更一般地说,表达式-语句的区别,以及相关的特性,如变异方法(例如,list.sort)返回None而不是self,导致语言更简单、更常规。当然,这是有代价的,因为它会导致语言不那么流利。有一个明显的权衡,Python 已经达到了你所能达到的极限。 (与 JavaScript 相比,它几乎是相反的极端。)但大多数喜欢 Python 的人认为它做出了正确的权衡。

      【讨论】:

      • 是的,这就是我得出的结论。我也同意这使阅读代码变得困难,但我很好奇它是否可能。
      • 这个答案从 Python3.8 开始不再是最新的,它支持 walrus operator 在表达式中使用赋值。
      • 海象算子不支持属性,包括属性。
      • 见下面的解决方案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-09-09
      • 2014-04-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-07-12
      • 1970-01-01
      相关资源
      最近更新 更多