Pythonic псевдоним для переменной экземпляра?

У меня есть класс, в котором я храню данные в списке по причинам наследования. Я хотел бы знать, и я сделал свою долю поиска в Google, есть ли более чистый способ, кроме создания функций и свойств 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, сохраняя при этом возможность упаковывать и распаковывать, как указано выше.


person gsrunion    schedule 24.01.2012    source источник
comment
Это вообще не проблема, но я собираюсь много раз создавать подклассы Ping и добавлять участников.   -  person gsrunion    schedule 24.01.2012
comment
Добро пожаловать в StackOverflow. Я отформатировал ваш код для вас (кнопка {}). Первое переформатирование бесплатно ;-)   -  person Johnsyweb    schedule 24.01.2012
comment
Спасибо, @Johnsyweb больше этого не допустит :)   -  person gsrunion    schedule 24.01.2012
comment
Это не отвечает на заданный вами вопрос, но может решить вашу проблему: не пишите собственную сериализацию, используйте Pickle.   -  person Johnsyweb    schedule 24.01.2012
comment
Могу ли я определить, как данные сериализуются с помощью рассола? Я должен следовать уже определенному двоичному формату.   -  person gsrunion    schedule 24.01.2012
comment
Этот ссылка имела работоспособное элегантное решение. Спасибо за комментарии.   -  person gsrunion    schedule 24.01.2012
comment
Остерегайтесь вызова функции create_id в определении метода — эта функция будет вызываться только один раз при разборе класса, а не при каждом вызове метода __init__.   -  person jsbueno    schedule 24.01.2012
comment
@GeraldStephanRunionII: что касается определения того, как это делается с помощью Pickle: нет, вы можете настроить множество вещей, касающихся травления и распаковки, но вы даете протоколу pickle словарь или другой стандартный тип данных, который затем сериализуется (и не некоторые разметки etxra на маринованной строке, чтобы отметить начало объекта и т. д., никто не может контролировать)   -  person jsbueno    schedule 24.01.2012


Ответы (2)


Если вместо этого вы храните свои значения/свойства в словаре:

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
person Rob Wouters    schedule 24.01.2012

Если ваша проблема заключается в том, чтобы просто сократить код для доступа к ident, вы можете просто использовать «свойство» в «старом стиле», то есть вы передаете ему в качестве параметров функции получения и установки вместо того, чтобы использовать его в качестве декоратора. .

В этом случае функции настолько малы, что могут быть лямбда-функциями, не влияющими на читабельность кода.

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)
person jsbueno    schedule 24.01.2012
comment
Это хорошее лаконичное решение. Да, имущество должно было уйти. Я использую предыдущее решение и перемещаю getattr setattr и getitem в мой базовый класс Serializable. Все подклассы будут очень краткими. - person gsrunion; 24.01.2012