Использование экземпляра класса в качестве атрибута, дескрипторов и свойств класса

недавно я заявил, что пытаюсь использовать новый стиль классов в Python (производных от object). В качестве выдержки, чтобы ознакомиться с ними, я пытаюсь определить класс, который имеет несколько экземпляров класса в качестве атрибутов, причем каждый из этих экземпляров класса описывает другой тип данных, например 1D-списки, 2d-массивы, скаляры и т. д. По сути, я хочу иметь возможность писать

some_class.data_type.some_variable

здесь data_type - экземпляр класса, описывающий коллекцию переменных. Ниже приведена моя первая попытка реализовать это, используя только profiles_1d экземпляр и довольно общие имена:

class profiles_1d(object):
    def __init__(self, x, y1=None, y2=None, y3=None):
        self.x = x
        self.y1 = y1
        self.y2 = y2
        self.y3 = y3

class collection(object):
    def __init__(self):
        self._profiles_1d = None

    def get_profiles(self):
        return self._profiles_1d

    def set_profiles(self, x, *args, **kwargs):
        self._profiles_1d = profiles_1d(x, *args, **kwargs)

    def del_profiles(self):
        self._profiles_1d = None

    profiles1d = property(fget=get_profiles, fset=set_profiles, fdel=del_profiles,
        doc="One dimensional profiles")

является ли приведенный выше код примерно подходящим способом решения этой проблемы. Примеры использования property просто установите значение некоторой переменной. Здесь я требую, чтобы мой метод set инициализировал экземпляр некоторого класса. Если нет, то были бы весьма признательны за любые другие предложения о лучших способах реализации этого.

кроме того, как я определение моего метода set ОК? Как правило, метод set, насколько я понимаю, определяет, что делать, когда пользователь, например,

collection.profiles1d = ...

единственный способ, которым я могу правильно установить атрибуты profiles_1d экземпляр с указанным выше кодом должен ввести collection.set_profiles([...], y1=[...], ...), но я думаю, что я не должен напрямую вызывать этот метод. В идеале я хотел бы ввести collection.profiles = ([...], y1=[...], ...): это правильно/возможно?

наконец, я видел декораторов, упомянутых много с repect к новому стилю о занятиях, но об этом я знаю очень мало. Уместно ли здесь использование декораторов? Это то, что я должен знать больше об этой проблеме?

2 ответов


во-первых, это хорошо, что вы изучаете классы нового стиля. У них много преимуществ.

современный способ создания свойств в Python:

class Collection(object):
    def __init__(self):
        self._profiles_1d = None

    @property
    def profiles(self):
        """One dimensional profiles"""
        return self._profiles_1d

    @profiles.setter
    def profiles(self, argtuple):
        args, kwargs = argtuple
        self._profiles_1d = profiles_1d(*args, **kwargs)

    @profiles.deleter
    def profiles(self):
        self._profiles_1d = None

затем установить profiles делать

collection = Collection()
collection.profiles = (arg1, arg2, arg3), {'kwarg1':val1, 'kwarg2':val2}

обратите внимание, что все три метода имеют одинаковое имя.

это обычно не делается; либо они передают атрибуты collections конструктор или у них создать profiles_1d сами и тогда делайте collections.profiles = myprofiles1d или передайте его в конструктор.

если вы хотите, чтобы атрибут управление доступом к сам вместо класса, управляющего доступом к атрибуту, сделайте атрибут классом с дескриптором. Сделайте это, если, в отличие от приведенного выше примера свойства, вы действительно хотите, чтобы данные хранились внутри атрибута (вместо другой переменной экземпляра faux-private). Кроме того, это хорошо, если вы собираетесь использовать одно и то же свойство снова и снова-сделайте его дескриптором, и вам не нужно писать код несколько раз или использовать базовый класс.

мне действительно нравится Страница @S. Lott -- Building Skills в Python's атрибуты, свойства и дескрипторы.


при создании propertys (или другие дескрипторы), которые должны вызывать другие методы экземпляра, соглашение об именах должно добавить _ к этим методам; так что ваши имена выше будут _get_profiles, _set_profiles и _del_profiles.

в Python 2.6+ каждое свойство также является декоратором, поэтому вам не нужно создавать (в противном случае бесполезно) _name методы:

@property
def test(self):
    return self._test

@test.setter
def test(self, newvalue):
    # validate newvalue if necessary
    self._test = newvalue

@test.deleter
def test(self):
    del self._test

похоже, что ваш код пытается установить profiles в классе вместо экземпляров -- если это таким образом, свойства класса не будут работать как collections.profiles будет заменено profiles_1d объект, clobbering свойство... если это действительно то, что вы хотите, вам придется сделать метакласс и поместить туда свойство.

надеюсь, вы говорите об экземплярах, поэтому класс будет выглядеть так:

class Collection(object):  # notice the capital C in Collection
    def __init__(self):
        self._profiles_1d = None

    @property
    def profiles1d(self):
        "One dimensional profiles"
        return self._profiles_1d

    @profiles1d.setter
    def profiles1d(self, value):
        self._profiles_1d = profiles_1d(*value)

    @profiles1d.deleter
    def profiles1d(self):
        del self._profiles_1d

и тогда вы бы сделали что-то вроде:

collection = Collection()
collection.profiles1d = x, y1, y2, y3

пара вещей, чтобы отметить:setter метод вызывается только с двумя пунктами: self и новая value (именно поэтому вам нужно было позвонить set_profiles1d вручную); при выполнении назначения именование ключевых слов не является опцией (это работает только в вызовах функций, которые не являются назначением). Если это имеет смысл для вас, вы можете получить фантазии и сделать что-то вроде:

collection.profiles1d = (x, dict(y1=y1, y2=y2, y3=y3))

а затем измените setter в:

    @profiles1d.setter
    def profiles1d(self, value):
        x, y = value
        self._profiles_1d = profiles_1d(x, **y)

который все еще довольно читаем (хотя я предпочитаю x, y1, y2, y3 версия себя).