Использование экземпляра класса в качестве атрибута, дескрипторов и свойств класса
недавно я заявил, что пытаюсь использовать новый стиль классов в 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}
обратите внимание, что все три метода имеют одинаковое имя.
это обычно не делается; либо они передают атрибуты collection
s конструктор или у них создать profiles_1d
сами и тогда делайте collections.profiles = myprofiles1d
или передайте его в конструктор.
если вы хотите, чтобы атрибут управление доступом к сам вместо класса, управляющего доступом к атрибуту, сделайте атрибут классом с дескриптором. Сделайте это, если, в отличие от приведенного выше примера свойства, вы действительно хотите, чтобы данные хранились внутри атрибута (вместо другой переменной экземпляра faux-private). Кроме того, это хорошо, если вы собираетесь использовать одно и то же свойство снова и снова-сделайте его дескриптором, и вам не нужно писать код несколько раз или использовать базовый класс.
мне действительно нравится Страница @S. Lott -- Building Skills в Python's атрибуты, свойства и дескрипторы.
при создании property
s (или другие дескрипторы), которые должны вызывать другие методы экземпляра, соглашение об именах должно добавить _
к этим методам; так что ваши имена выше будут _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
версия себя).