【问题标题】:Dynamically creating properties of a class动态创建类的属性
【发布时间】:2019-05-02 18:32:59
【问题描述】:

看看这段代码sn-p:

class Face():
    pass

class Cube():
    def __init__(self):
        self.faces = {
                'front': Face(1),
                ...
                }

    @property
    def front(self):
        return self.faces['front']

    @front.setter
    def front(self, f):
        pass

我已经为所有的面创建了 getter 和 setter。有没有办法让这段代码更紧凑,也许是通过动态创建 getter 和 setter?

【问题讨论】:

  • 为什么你把'front'放在字典里而不是你的Cube类的直接属性?那么就不需要属性了。
  • @CorentinLimier 我假设 OP 没有在 self.faces 中显示他们想要的键的完整 setter 和 getter 逻辑。

标签: python oop dynamic properties


【解决方案1】:

假设这就是你的课程所做的一切,你可以做类似的事情

class Cube:
   ...

def __getattr__(self, name):
    return self.faces[name]

def __setattr__(self, name, value):
    self.faces[name] = value

【讨论】:

    【解决方案2】:

    如果你真的想这样做,你可以使用__getattr____setattr__

    class Cube:
    
       ...   
    
       def __getattr__(self, item):
            return self.faces[item]
    
       def __setattr__(self, item, value):
            self.faces[item] = value
    

    但是当您在__init__ 方法中设置front 时,您也可以将其设为普通成员...

    【讨论】:

      【解决方案3】:

      以下代码假设您

      • 有理由拥有self.faces dict,而不是直接在实例上设置front 之类的属性

      • 和/或想要为self.faces 中的键实现一些有意义的getter 和setter 逻辑。

      否则,这个练习毫无意义,因为正如 Corentin Limier 所说,您可以简单地设置 self.front = Face(1),等等。


      您可以使用描述符、保存人脸名称的类变量和类装饰器。将描述符视为可重用的属性。

      在下面的示例代码中,我将num 实例变量添加到Face 和面部'side',仅用于演示目的。

      class FaceDescriptor:
          def __get__(self, instance, owner):
              # your custom getter logic
      
              # dummy implementation
              if instance is not None:                
                  return instance.faces[self.face]
      
          def __set__(self, instance, value):
              # your custom setter logic
      
              # dummy implementation
              instance.faces[self.face] = value
      
      def set_faces(cls):
           for face in cls._faces:
               desc = FaceDescriptor()
               desc.face = face
               setattr(cls, face, desc)
           return cls
      
      class Face():
          def __init__(self, num):
              self.num = num   
      
      @set_faces
      class Cube():
          _faces = ['front', 'side']
      
          def __init__(self):
              self.faces = {face:Face(i) for i, face in enumerate(self._faces, 1)}
      

      在行动:

      >>> c = Cube()                                                                                                                                                                                         
      >>> c.front.num                                                                                                                                                                                         
      1
      >>> c.side.num                                                                                                                                                                                          
      2
      >>> c.front = 'stuff'                                                                                                                                                                                   
      >>> c.front                                                                                                                                                                                             
      'stuff'
      >>> c.faces                                                                                                                                                                                             
      {'front': 'stuff', 'side': <__main__.Face at 0x7fd0978f37f0>}
      

      【讨论】:

      • 实际上,如果我可以遍历面,使用这个类会更容易。现在我看到实施__iter__ 可以解决这个问题。
      • @saga 这与如何编写动态属性完全不同。
      【解决方案4】:

      您的代码是多余的,因为实例 attributes 已经存储在 __dict__ 属性的字典中。我认识到您专注于以更少的行数编写代码。保持自己的成长是一个很好的挑战,但从长远来看,您应该专注于代码的清晰性。

      这是一种无需使用属性即可编写代码的更简单方法:

      class Face():
          pass
      
      class Cube():
          def __init__(self):
              self.front = Face()
              self.rear = Face()
      

      封装的原则是你应该将你的“属性”隐藏在“属性”后面。尽管这在 python 中没有得到严格执行,但这样做也不是一个坏主意。以下是正确的做法:

      class Face():
          pass
      
      class Cube():
          def __init__(self):
              self._front = Face()
      
          @property
          def front(self):
              return self._front
      
          @front.setter
          def front(self, value):
              self._front = value
      

      最后回答您的问题,是的,您可以动态创建属性。 https://stackoverflow.com/a/1355444/3368572

      但请记住,应为特殊情况保留编写动态代码,因为这会使您的 IDE 更难以跟踪您的程序流程。如果您按预期使用约定,那么您的代码对于人们和您的 IDE 来说都是不言自明的。

      【讨论】:

      • 我假设 OP 没有在 self.faces 中显示他们想要的键的完整 setter 和 getter 逻辑。
      猜你喜欢
      • 2020-01-08
      • 2017-07-14
      • 1970-01-01
      • 1970-01-01
      • 2017-11-07
      • 2018-12-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多