【问题标题】:Pythonic alias for instance variable?实例变量的 Pythonic 别名?
【发布时间】:2012-01-24 01:57:35
【问题描述】:

我有一个类,出于继承原因,我将数据存储在列表中。我想知道,并且我已经完成了谷歌搜索,除了创建 getter/setter 函数和属性来为该列表中的元素提供别名之外,是否还有更简洁的方法?

例如...

class Serializable(object):
    """Adds serialization to from binary string"""

    def encode(self):
        """Pack into struct"""
        return self.encoder.pack(*self)

    def decode(self, data_str):
        """Unpack from struct"""
        self.data = self.encoder.unpack(data_str)
        return self.data


class Ping(Serializable):

    encoder = Struct("!16sBBBL")

    def __init__(self, ident=create_id(), ttl=TTL, hops=0, length=0):
        self.data = [ident, 1, ttl, hops, length]
        self.ident = property(self.data[0])

    def __getitem__(self, index):
        return self.data[index]

    @property
    def ident(self):
        return self.data[0]

    @ident.setter
    def ident(self, value):
        self.data[0] = value

    @property
    def protocol(self):
        return self.data[1]

    @protocol.setter
    def protocol(self, protocol):
        self.data[1]

我希望使用更紧凑的解决方案来引用 object.ident,同时保持上述打包和解包的能力。

【问题讨论】:

  • 根本不是问题,但我将进一步对 Ping 进行多次子类化并添加成员。
  • 欢迎来到 StackOverflow。我为您格式化了您的代码(“{}”按钮)。第一次重新格式化是免费的 ;-)
  • 谢谢@Johnsyweb 不会让它再次发生 :)
  • 这并不能回答您提出的问题,但它可能会解决您的问题:不要编写自己的序列化,使用Pickle
  • 我可以定义如何通过 pickle 序列化数据吗?我必须遵循已定义的二进制格式。

标签: python properties alias


【解决方案1】:

如果您将值/属性存储在字典中:

def __init__(self, ident=create_id(), ttl=TTL, hops=0, length=0):
    self.data = {
        'ident': ident,
        'protocol': 1,
        'ttl': hops,
        'length': length,
    }

然后覆盖__getattr____setattr__

def __getattr__(self, attr):
    return self.data[attr]
def __setattr__(self, attr, value):
    if attr == 'data':
        object.__setattr__(self, attr, value)
    else:
        self.data[attr] = value

现在你可以这样做了:

>>> ping = Ping()
>>> ping.protocol
1
>>> ping.protocol = 2
>>> ping.protocol
2

如果self.data 必须是一个列表,你可以这样做:

class Ping(Serializable):

    mapping = ('ident', 'protocol', 'ttl', 'hops', 'length')

    encoder = Struct("!16sBBBL")

    def __init__(self, ident=create_id(), ttl=TTL, hops=0, length=0):
        self.data = [ident, 1, ttl, hops, length]

    def __getitem__(self, index):
        return self.data[index]

    def __getattr__(self, attr):
        index = self.mapping.index(attr)
        return self.data[index]

    def __setattr__(self, attr, value):
        if attr == 'data':
            object.__setattr__(self, attr, value)
        else:
            index = self.mapping.index(attr)
            self.data[index] = value

【讨论】:

    【解决方案2】:
    def alias_property(key):
        return property(
            lambda self: getattr(self, key),
            lambda self, val: setattr(self, key, val),
            lambda self: delattr(self, key))
    
    class A(object):
    
        def __init__(self, prop):
            self.prop = prop
    
        prop_alias = alias_property('prop')
    

    【讨论】:

      【解决方案3】:

      如果您的问题只是缩短访问ident 的代码,您可以只使用“旧样式”中的“属性”——也就是说,您将getter 和setter 函数作为参数传递给它,而不是将其用作装饰器。

      在这种情况下,函数是如此之小,它们可以是 lambda 函数,而不会影响代码的可读性。

      class Ping(Serializable):
      
          encoder = Struct("!16sBBBL")
      
          def __init__(self, ident=None, ttl=TTL, hops=0, length=0):
              if ident is None:
                  ident = create_id()
              self.data = [ident, 1, ttl, hops, length]
              # The line bellow looks like garbage -
              # it does not even make sense as a call to `property`
              # should have a callable as first parameter
              # returns an object that is designed to work as a class attribute
              # self.ident = property(self.data[0])
              # rather:
              self.ident = ident 
              # this will use the property defined bellow
      
          def __getitem__(self, index):
              return self.data[index]
      
          ident = property(lambda s: s.data[0], lambda s, v: s.data[0].__setitem__(0, v)
          protocol = property(lambda s: s.data[1], lambda s, v: s.data[1].__setitem__(1, v)
      

      【讨论】:

      • 这是一个简洁的解决方案。是的,财产必须离开。我将采用以前的解决方案并将 getattr setattrgetitem 移动到我的 Serializable 基类。所有子类都非常简短。
      猜你喜欢
      • 1970-01-01
      • 2018-10-15
      • 2014-02-07
      • 1970-01-01
      • 2011-05-07
      • 2020-05-09
      • 1970-01-01
      • 2017-02-15
      • 1970-01-01
      相关资源
      最近更新 更多