Как добавить свойство в класс динамически?
Цель состоит в том, чтобы создать макет класса, который ведет себя как результирующий набор БД.
Так, например, если запрос базы данных возвращает, используя выражение dict,{'ab':100, 'cd':200}
, то я хотел бы видеть:
>>> dummy.ab
100
Сначала я подумал, что, возможно, смогу сделать это следующим образом:
ks = ['ab', 'cd']
vs = [12, 34]
class C(dict):
def __init__(self, ks, vs):
for i, k in enumerate(ks):
self[k] = vs[i]
setattr(self, k, property(lambda x: vs[i], self.fn_readyonly))
def fn_readonly(self, v)
raise "It is ready only"
if __name__ == "__main__":
c = C(ks, vs)
print c.ab
Но c.ab
возвращает объект свойства вместо этого.
Замена строки setattr
на k = property(lambda x: vs[i])
совершенно бесполезна.
Итак, как правильно создать свойство экземпляра во время выполнения?
P.S. Я в курсе альтернативы, представленной в Как используется метод __getattribute__
?
18 ответов:
Я полагаю, что должен расширить этот ответ, теперь, когда я старше и мудрее и знаю, что происходит. Лучше поздно чем никогда.
Вы можете добавить свойство в класс динамически. Но вот в чем загвоздка: вы должны добавить его в класс .
>>> class Foo(object): ... pass ... >>> foo = Foo() >>> foo.a = 3 >>> Foo.b = property(lambda self: self.a + 1) >>> foo.b 4
A
property
На самом деле является простой реализацией вещи, называемой дескриптором . Это объект, который обеспечивает пользовательскую обработку для данного атрибута, для данного класса. Вроде как способ ... фактор огромное деревоif
из__getattribute__
.Когда я спрашиваю
foo.b
в приведенном выше примере, Python видит, чтоb
, определенный в классе, реализует протокол дескриптора -что просто означает, что это объект с__get__
,__set__
, или метод__delete__
. Дескриптор берет на себя ответственность за обработку этого атрибута, поэтому Python вызываетFoo.b.__get__(foo, Foo)
, и возвращаемое значение передается вам как значение атрибута. В случаеproperty
Каждый из этих методов просто вызываетfget
,fset
, илиfdel
вы передали конструкторуproperty
.Дескрипторы-это действительно способ Python раскрыть водопровод всей его реализации OO. На самом деле, есть другой тип дескриптора, даже более распространенный, чем
property
.Скромный метод - это просто другой вид дескриптора. Его>>> class Foo(object): ... def bar(self): ... pass ... >>> Foo().bar <bound method Foo.bar of <__main__.Foo object at 0x7f2a439d5dd0>> >>> Foo().bar.__get__ <method-wrapper '__get__' of instancemethod object at 0x7f2a43a8a5a0>
__get__
прикрепляется к вызывающему экземпляру в качестве первого аргумента; фактически, он делает это:def __get__(self, instance, owner): return functools.partial(self.function, instance)
Во всяком случае, я подозреваю, что именно поэтому дескрипторы работают только на классах: они формализация того материала, который питает классы в первую очередь. Они даже являются исключением из правила: очевидно, что вы можете назначить дескрипторы классу, А классы сами являются экземплярами
type
! На самом деле, попытка прочитатьFoo.b
по-прежнему вызываетproperty.__get__
; это просто идиоматично для дескрипторов, возвращающих себя при обращении к атрибутам класса.Я думаю, что это довольно круто, что практически вся система OO Python может быть выражена в Python. :)
О, и я написал многословный пост в блоге о дескрипторах некоторое время назад, если вам интересно.
Цель состоит в том, чтобы создать макет класса, который ведет себя как результирующий набор БД.
Итак, вам нужен словарь, в котором вы можете написать a['b'] как a. b?
Это просто:
class atdict(dict): __getattr__= dict.__getitem__ __setattr__= dict.__setitem__ __delattr__= dict.__delitem__
Кажется, вы могли бы решить эту проблему гораздо проще с помощью
namedtuple
, так как вы заранее знаете весь список полей.from collections import namedtuple Foo = namedtuple('Foo', ['bar', 'quux']) foo = Foo(bar=13, quux=74) print foo.bar, foo.quux foo2 = Foo() # error
Если вам абсолютно необходимо написать свой собственный сеттер, вам придется делать метапрограммирование на уровне класса;
property()
не работает на экземплярах.
Для этого не нужно использовать свойство. Просто переопределите
__setattr__
, чтобы сделать их только для чтения.class C(object): def __init__(self, keys, values): for (key, value) in zip(keys, values): self.__dict__[key] = value def __setattr__(self, name, value): raise Exception("It is read only!")
Тада.
>>> c = C('abc', [1,2,3]) >>> c.a 1 >>> c.b 2 >>> c.c 3 >>> c.d Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'C' object has no attribute 'd' >>> c.d = 42 Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 6, in __setattr__ Exception: It is read only! >>> c.a = 'blah' Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 6, in __setattr__ Exception: It is read only!
Нельзя добавить новый
property()
в экземпляр во время выполнения, поскольку свойства являются дескрипторами данных. Вместо этого необходимо динамически создать новый класс или перегрузить__getattribute__
для обработки дескрипторов данных в экземплярах.
Я задал аналогичный вопрос на этом посте переполнения стека, чтобы создать фабрику классов, которая создавала простые типы. Результатом был Этот ответ, который имел рабочую версию фабрики класса. Вот фрагмент ответа:
def Struct(*args, **kwargs): def init(self, *iargs, **ikwargs): for k,v in kwargs.items(): setattr(self, k, v) for i in range(len(iargs)): setattr(self, args[i], iargs[i]) for k,v in ikwargs.items(): setattr(self, k, v) name = kwargs.pop("name", "MyStruct") kwargs.update(dict((k, None) for k in args)) return type(name, (object,), {'__init__': init, '__slots__': kwargs.keys()}) >>> Person = Struct('fname', 'age') >>> person1 = Person('Kevin', 25) >>> person2 = Person(age=42, fname='Terry') >>> person1.age += 10 >>> person2.age -= 10 >>> person1.fname, person1.age, person2.fname, person2.age ('Kevin', 35, 'Terry', 32) >>>
Вы можете использовать некоторую вариацию этого, чтобы создать значения по умолчанию, которые являются вашей целью (есть также ответ в этом вопросе, который имеет дело с этим).
Не уверен, что я полностью понимаю вопрос, но вы можете изменить свойства экземпляра во время выполнения с помощью встроенного
__dict__
вашего класса:class C(object): def __init__(self, ks, vs): self.__dict__ = dict(zip(ks, vs)) if __name__ == "__main__": ks = ['ab', 'cd'] vs = [12, 34] c = C(ks, vs) print(c.ab) # 12
Предположим, у вас есть объект, к которому вы хотите добавить свойство. Обычно я хочу использовать свойства, когда мне нужно начать управлять доступом к атрибуту в коде, который имеет нисходящее использование, чтобы я мог поддерживать согласованный API. Теперь я обычно добавляю их в исходный код, где определен объект, но предположим, что у вас нет такого доступа, или вам нужно действительно динамически выбирать свои функции программно.Как динамически добавить свойство в класс python?
Создайте класс
Используя пример, основанный на документации для
property
, давайте создадим класс объекта с" скрытым " атрибутом и создадим его экземпляр:class C(object): '''basic class''' _x = None o = C()
В Python мы ожидаем, что есть один очевидный способ делать вещи. Однако в данном случае я собираюсь показать два способа: с декораторской нотацией и без нее. Во-первых, без декораторской нотации. Это может быть более полезно для динамического назначения геттеров, сеттеров или deleters.
Динамический (он же Monkey Patching)
Давайте создадим некоторые для нашего класса:
И теперь мы присваиваем их свойству. Обратите внимание, что мы могли бы выбрать наши функции программно здесь, отвечая на динамический вопрос:def getx(self): return self._x def setx(self, value): self._x = value def delx(self): del self._x
C.x = property(getx, setx, delx, "I'm the 'x' property.")
И использование:
>>> o.x = 'foo' >>> o.x 'foo' >>> del o.x >>> print(o.x) None >>> help(C.x) Help on property: I'm the 'x' property.
Декораторы
Мы могли бы сделать то же самое, что мы сделали выше с нотацией декоратора, но в этом случае мы должны назвать методы все тем же именем (и я бы рекомендовал сохранить его то же, что и атрибут), поэтому программное присвоение не так тривиально, как это делается с помощью приведенного выше метода:
@property def x(self): '''I'm the 'x' property.''' return self._x @x.setter def x(self, value): self._x = value @x.deleter def x(self): del self._x
И назначить объект свойства с его подготовленными сеттерами и удалителями классу:
C.x = x
И использование:
>>> help(C.x) Help on property: I'm the 'x' property. >>> o.x >>> o.x = 'foo' >>> o.x 'foo' >>> del o.x >>> print(o.x) None
Лучший способ достичь этого-определить
__slots__
. Таким образом, ваши экземпляры не могут иметь новых атрибутов.ks = ['ab', 'cd'] vs = [12, 34] class C(dict): __slots__ = [] def __init__(self, ks, vs): self.update(zip(ks, vs)) def __getattr__(self, key): return self[key] if __name__ == "__main__": c = C(ks, vs) print c.ab
Что печатает
12
c.ab = 33
Что дает:
AttributeError: 'C' object has no attribute 'ab'
Просто еще один пример, как достичь желаемого эффекта
class Foo(object): _bar = None @property def bar(self): return self._bar @bar.setter def bar(self, value): self._bar = value def __init__(self, dyn_property_name): setattr(Foo, dyn_property_name, Foo.bar)
Так что теперь мы можем делать такие вещи, как:
>>> foo = Foo('baz') >>> foo.baz = 5 >>> foo.bar 5 >>> foo.baz 5
Для тех, кто приходит из поисковых систем, вот две вещи, которые я искал, когда говорил о динамических свойствах:
class Foo: def __init__(self): # we can dynamically have access to the properties dict using __dict__ self.__dict__['foo'] = 'bar' assert Foo().foo == 'bar' # or we can use __getattr__ and __setattr__ to execute code on set/get class Bar: def __init__(self): self._data = {} def __getattr__(self, key): return self._data[key] def __setattr__(self, key, value): self._data[key] = value bar = Bar() bar.foo = 'bar' assert bar.foo == 'bar'
__dict__
Хорошо, если вы хотите поставить динамически создаваемые свойства.__getattr__
хорошо делать что-то только тогда, когда это значение необходимо, например, запрашивать базу данных. Комбинация set / get хороша для упрощения доступа к данным, хранящимся в классе (как в примере выше).Если вам нужно только одно динамическое свойство, посмотрите на свойство () встроенная функция.
Это, кажется, работает(но смотрите ниже):
class data(dict,object): def __init__(self,*args,**argd): dict.__init__(self,*args,**argd) self.__dict__.update(self) def __setattr__(self,name,value): raise AttributeError,"Attribute '%s' of '%s' object cannot be set"%(name,self.__class__.__name__) def __delattr__(self,name): raise AttributeError,"Attribute '%s' of '%s' object cannot be deleted"%(name,self.__class__.__name__)
Если вам нужно более сложное поведение, не стесняйтесь редактировать свой ответ.
Править
Для больших наборов данных, вероятно, было бы более эффективно использовать память:
class data(dict,object): def __init__(self,*args,**argd): dict.__init__(self,*args,**argd) def __getattr__(self,name): return self[name] def __setattr__(self,name,value): raise AttributeError,"Attribute '%s' of '%s' object cannot be set"%(name,self.__class__.__name__) def __delattr__(self,name): raise AttributeError,"Attribute '%s' of '%s' object cannot be deleted"%(name,self.__class__.__name__)
Чтобы ответить на основной вопрос, вам нужен атрибут только для чтения из dict в качестве неизменяемого источника данных:
Цель состоит в том, чтобы создать макет класса, который ведет себя как результирующий набор БД.
Так, например, если запрос базы данных возвращает, используя выражение dict,
{'ab':100, 'cd':200}
, то я хотел бы видеть>>> dummy.ab 100
Я покажу, как использовать
namedtuple
из модуляcollections
для выполнения именно этого:import collections data = {'ab':100, 'cd':200} def maketuple(d): '''given a dict, return a namedtuple''' Tup = collections.namedtuple('TupName', d.keys()) # iterkeys in Python2 return Tup(**d) dummy = maketuple(data) dummy.ab
Возвращает
100
class atdict(dict): def __init__(self, value, **kwargs): super().__init__(**kwargs) self.__dict = value def __getattr__(self, name): for key in self.__dict: if type(self.__dict[key]) is list: for idx, item in enumerate(self.__dict[key]): if type(item) is dict: self.__dict[key][idx] = atdict(item) if type(self.__dict[key]) is dict: self.__dict[key] = atdict(self.__dict[key]) return self.__dict[name] d1 = atdict({'a' : {'b': [{'c': 1}, 2]}}) print(d1.a.b[0].c)
И вывод:
>> 1
Для обновления атрибутов класса с помощью объекта справочника можно использовать следующий код:
class ExampleClass(): def __init__(self, argv): for key, val in argv.items(): self.__dict__[key] = val if __name__ == '__main__': argv = {'intro': 'Hello World!'} instance = ExampleClass(argv) print instance.intro
Многие из представленных ответов требуют так много строк на свойство, т. е. / И / ИЛИ - то, что я бы счел уродливой или утомительной реализацией из-за повторяемости, необходимой для нескольких свойств и т. д. Я предпочитаю продолжать кипятить вещи / упрощать их до тех пор, пока они больше не могут быть упрощены или пока это не будет служить большой цели.
Короче говоря: в завершенных работах, если я повторяю 2 строки кода, я обычно преобразую его в однострочную вспомогательную функцию и т. д... Я упростите математику или нечетные аргументы, такие как ( start_x, start_y, end_x, end_y ) до ( x, y, w, h), то есть x, y, x + w, y + h ( иногда требуется min / max или если w / h отрицательны и реализации это не нравится, я вычитаю из x / y и abs w / h. и т. д.. ).Переопределение внутренних геттеров / сеттеров-это нормальный способ, но проблема в том, что вам нужно сделать это для каждого класса или родительского класса для этой базы... Это не работает для меня, так как я предпочитаю быть свободным в выборе детей. / родители для наследования, дочерние узлы и т. д.
Я создал решение, которое отвечает на вопрос, не используя тип данных Dict для предоставления данных, поскольку я нахожу, что это утомительно вводить данные и т. д...Мое решение требует, чтобы вы добавили 2 дополнительные строки над вашим классом, чтобы создать базовый класс для класса, в который вы хотите добавить Свойства, затем по 1 строке, и у вас есть возможность добавлять обратные вызовы для управления данными, информировать вас, когда данные изменяются, ограничивать данные, которые могут быть изменены. набор на основе значения и / или типа данных, и многое другое.
У вас также есть возможность использовать _object.х, _object.x = значение, _object.GetX ( ), _object.SetX (значение) и они обрабатываются эквивалентно.
Кроме того, значения являются единственными нестатическими данными, которые назначаются экземпляру класса, но фактическое свойство назначается классу, то есть вещи, которые вы не хотите повторять, не должны повторяться... Вы можете назначить значение по умолчанию, чтобы геттеру оно не понадобилось. время, хотя есть возможность переопределить значение по умолчанию по умолчанию, и есть еще один вариант, так что геттер возвращает необработанное сохраненное значение путем переопределения значений по умолчанию ( Примечание: этот метод означает, что необработанное значение назначается только тогда, когда значение назначено, в противном случае это не так - когда значение сброшено, то он назначает None и т. д.. )
Есть также много вспомогательных функций - первое свойство, которое добавляется добавляет 2 или около того помощников к классу для ссылки на значения экземпляра... Они являются ResetAccessors( _key, .. ) varargs repeated (все они могут быть повторены с использованием первых именованных args ) и SetAccessors( _key, _value ) с возможностью добавления большего количества в основной класс для повышения эффективности - планируются следующие: способ группировать методы доступа вместе, так что если вы склонны сбрасывать несколько одновременно, каждый раз, вы можете назначить их группе и сбросить группу вместо повторения именованных ключей каждый раз, и многое другое.
Экземпляр / необработанное сохраненное значение хранится в класс., класс__. ссылается на класс доступа, который содержит статические переменные / значения / функции свойства. _класс. это само свойство, которое вызывается при обращении через класс экземпляра во время установки / получения и т. д.
Accessor _ Class.__ указывает на класс, но так как он является внутренним, он должен быть назначен В класса, который является, почему я выбрал, чтобы использовать __наименование = AccessorFunc( ... ) чтобы назначить его, одна строка на свойство с множеством необязательных аргументов для использования ( использование ключевых вараргов, потому что их легче и эффективнее идентифицировать и поддерживать )...
Я также создаю много функций, как уже упоминалось, некоторые из которых используют информацию о функции доступа , поэтому ее не нужно вызывать ( так как это немного неудобно в данный момент - прямо сейчас вам нужно использовать _class..FunctionName( _class_instance, args ) - я обошел с помощью стека / трассировки, чтобы захватить ссылку на экземпляр, чтобы захватить значение, добавив функции, которые либо выполняют это битовый марафон, или путем добавления методов доступа к объекту и использования self (названный так, чтобы указать, что они предназначены для экземпляра и сохранить доступ к self, ссылку на класс AccessorFunc и другую информацию из определений функций).
Это не совсем сделано, но это фантастический захват ног. Примечание: Если вы не используете _ _ Name = AccessorFunc( ... ) чтобы создать свойства, у вас не будет доступа к ключу__, даже если я определяю его в функции init. Если вы это сделаете, то там нет никаких проблем.
Также: обратите внимание, что имя и ключ различны... Имя является "формальным", используется при создании имени функции, а ключ предназначен для хранения и доступа к данным. то есть _класс.x, где нижний регистр x-ключ, имя будет прописным X, так что GetX( ) является функцией вместо Getx (), что выглядит немного странно. это позволяет самому себе.x работать и выглядеть соответствующим, но также разрешить GetX( ) и выглядеть соответствующим.
У меня есть пример класса, настроенного с ключом / именем, идентичным и отличающимся, чтобы показать. один множество вспомогательных функций, созданных для вывода данных (Примечание: не все из этого является полным), так что вы можете видеть, что происходит.
Текущий список функций, использующих ключ: x, name: X выводится как:
Это далеко не полный список - есть несколько,которые не попали в него на момент публикации...
_instance.SetAccessors( _key, _value [ , _key, _value ] .. ) Instance Class Helper Function: Allows assigning many keys / values on a single line - useful for initial setup, or to minimize lines. In short: Calls this.Set<Name>( _value ) for each _key / _value pairing. _instance.ResetAccessors( _key [ , _key ] .. ) Instance Class Helper Function: Allows resetting many key stored values to None on a single line. In short: Calls this.Reset<Name>() for each name provided. Note: Functions below may list self.Get / Set / Name( _args ) - self is meant as the class instance reference in the cases below - coded as this in AccessorFuncBase Class. this.GetX( _default_override = None, _ignore_defaults = False ) GET: Returns IF ISSET: STORED_VALUE .. IF IGNORE_DEFAULTS: None .. IF PROVIDED: DEFAULT_OVERRIDE ELSE: DEFAULT_VALUE 100 this.GetXRaw( ) RAW: Returns STORED_VALUE 100 this.IsXSet( ) ISSET: Returns ( STORED_VALUE != None ) True this.GetXToString( ) GETSTR: Returns str( GET ) 100 this.GetXLen( _default_override = None, _ignore_defaults = False ) LEN: Returns len( GET ) 3 this.GetXLenToString( _default_override = None, _ignore_defaults = False ) LENSTR: Returns str( len( GET ) ) 3 this.GetXDefaultValue( ) DEFAULT: Returns DEFAULT_VALUE 1111 this.GetXAccessor( ) ACCESSOR: Returns ACCESSOR_REF ( self.__<key> ) [ AccessorFuncBase ] Key: x : Class ID: 2231452344344 : self ID: 2231448283848 Default: 1111 Allowed Types: {"<class 'int'>": "<class 'type'>", "<class 'float'>": "<class 'type'>"} Allowed Values: None this.GetXAllowedTypes( ) ALLOWED_TYPES: Returns Allowed Data-Types {"<class 'int'>": "<class 'type'>", "<class 'float'>": "<class 'type'>"} this.GetXAllowedValues( ) ALLOWED_VALUES: Returns Allowed Values None this.GetXHelpers( ) HELPERS: Returns Helper Functions String List - ie what you're reading now... THESE ROWS OF TEXT this.GetXKeyOutput( ) Returns information about this Name / Key ROWS OF TEXT this.GetXGetterOutput( ) Returns information about this Name / Key ROWS OF TEXT this.SetX( _value ) SET: STORED_VALUE Setter - ie Redirect to __<Key>.Set N / A this.ResetX( ) RESET: Resets STORED_VALUE to None N / A this.HasXGetterPrefix( ) Returns Whether or Not this key has a Getter Prefix... True this.GetXGetterPrefix( ) Returns Getter Prefix... Get this.GetXName( ) Returns Accessor Name - Typically Formal / Title-Case X this.GetXKey( ) Returns Accessor Property Key - Typically Lower-Case x this.GetXAccessorKey( ) Returns Accessor Key - This is to access internal functions, and static data... __x this.GetXDataKey( ) Returns Accessor Data-Storage Key - This is the location where the class instance value is stored.. _x
Некоторые из выводимых данных:
Это для совершенно нового класса, созданного с использованием демонстрационного класса без каких-либо данных, назначенных, кроме имя (чтобы его можно было вывести), которое является _foo, именем переменной, которое я использовал...
_foo --- MyClass: ---- id( this.__class__ ): 2231452349064 :::: id( this ): 2231448475016 Key Getter Value | Raw Key Raw / Stored Value | Get Default Value Default Value | Get Allowed Types Allowed Types | Get Allowed Values Allowed Values | Name: _foo | _Name: _foo | __Name.DefaultValue( ): AccessorFuncDemoClass | __Name.GetAllowedTypes( ) <class 'str'> | __Name.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type | x: 1111 | _x: None | __x.DefaultValue( ): 1111 | __x.GetAllowedTypes( ) (<class 'int'>, <class 'float'>) | __x.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type | y: 2222 | _y: None | __y.DefaultValue( ): 2222 | __y.GetAllowedTypes( ) (<class 'int'>, <class 'float'>) | __y.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type | z: 3333 | _z: None | __z.DefaultValue( ): 3333 | __z.GetAllowedTypes( ) (<class 'int'>, <class 'float'>) | __z.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type | Blah: <class 'int'> | _Blah: None | __Blah.DefaultValue( ): <class 'int'> | __Blah.GetAllowedTypes( ) <class 'str'> | __Blah.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type | Width: 1 | _Width: None | __Width.DefaultValue( ): 1 | __Width.GetAllowedTypes( ) (<class 'int'>, <class 'bool'>) | __Width.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type | Height: 0 | _Height: None | __Height.DefaultValue( ): 0 | __Height.GetAllowedTypes( ) <class 'int'> | __Height.GetAllowedValues( ) (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) | Depth: 2 | _Depth: None | __Depth.DefaultValue( ): 2 | __Depth.GetAllowedTypes( ) Saved Value Restricted to Authorized Values ONLY | __Depth.GetAllowedValues( ) (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) | this.IsNameSet( ): True this.GetName( ): _foo this.GetNameRaw( ): _foo this.GetNameDefaultValue( ): AccessorFuncDemoClass this.GetNameLen( ): 4 this.HasNameGetterPrefix( ): <class 'str'> this.GetNameGetterPrefix( ): None this.IsXSet( ): False this.GetX( ): 1111 this.GetXRaw( ): None this.GetXDefaultValue( ): 1111 this.GetXLen( ): 4 this.HasXGetterPrefix( ): (<class 'int'>, <class 'float'>) this.GetXGetterPrefix( ): None this.IsYSet( ): False this.GetY( ): 2222 this.GetYRaw( ): None this.GetYDefaultValue( ): 2222 this.GetYLen( ): 4 this.HasYGetterPrefix( ): (<class 'int'>, <class 'float'>) this.GetYGetterPrefix( ): None this.IsZSet( ): False this.GetZ( ): 3333 this.GetZRaw( ): None this.GetZDefaultValue( ): 3333 this.GetZLen( ): 4 this.HasZGetterPrefix( ): (<class 'int'>, <class 'float'>) this.GetZGetterPrefix( ): None this.IsBlahSet( ): False this.GetBlah( ): <class 'int'> this.GetBlahRaw( ): None this.GetBlahDefaultValue( ): <class 'int'> this.GetBlahLen( ): 13 this.HasBlahGetterPrefix( ): <class 'str'> this.GetBlahGetterPrefix( ): None this.IsWidthSet( ): False this.GetWidth( ): 1 this.GetWidthRaw( ): None this.GetWidthDefaultValue( ): 1 this.GetWidthLen( ): 1 this.HasWidthGetterPrefix( ): (<class 'int'>, <class 'bool'>) this.GetWidthGetterPrefix( ): None this.IsDepthSet( ): False this.GetDepth( ): 2 this.GetDepthRaw( ): None this.GetDepthDefaultValue( ): 2 this.GetDepthLen( ): 1 this.HasDepthGetterPrefix( ): None this.GetDepthGetterPrefix( ): (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) this.IsHeightSet( ): False this.GetHeight( ): 0 this.GetHeightRaw( ): None this.GetHeightDefaultValue( ): 0 this.GetHeightLen( ): 1 this.HasHeightGetterPrefix( ): <class 'int'> this.GetHeightGetterPrefix( ): (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
И это после присвоения всем свойствам _foo (кроме имени) следующих значений в том же порядке: 'string ', 1.0, True, 9, 10, False
this.IsNameSet( ): True this.GetName( ): _foo this.GetNameRaw( ): _foo this.GetNameDefaultValue( ): AccessorFuncDemoClass this.GetNameLen( ): 4 this.HasNameGetterPrefix( ): <class 'str'> this.GetNameGetterPrefix( ): None this.IsXSet( ): True this.GetX( ): 10 this.GetXRaw( ): 10 this.GetXDefaultValue( ): 1111 this.GetXLen( ): 2 this.HasXGetterPrefix( ): (<class 'int'>, <class 'float'>) this.GetXGetterPrefix( ): None this.IsYSet( ): True this.GetY( ): 10 this.GetYRaw( ): 10 this.GetYDefaultValue( ): 2222 this.GetYLen( ): 2 this.HasYGetterPrefix( ): (<class 'int'>, <class 'float'>) this.GetYGetterPrefix( ): None this.IsZSet( ): True this.GetZ( ): 10 this.GetZRaw( ): 10 this.GetZDefaultValue( ): 3333 this.GetZLen( ): 2 this.HasZGetterPrefix( ): (<class 'int'>, <class 'float'>) this.GetZGetterPrefix( ): None this.IsBlahSet( ): True this.GetBlah( ): string Blah this.GetBlahRaw( ): string Blah this.GetBlahDefaultValue( ): <class 'int'> this.GetBlahLen( ): 11 this.HasBlahGetterPrefix( ): <class 'str'> this.GetBlahGetterPrefix( ): None this.IsWidthSet( ): True this.GetWidth( ): False this.GetWidthRaw( ): False this.GetWidthDefaultValue( ): 1 this.GetWidthLen( ): 5 this.HasWidthGetterPrefix( ): (<class 'int'>, <class 'bool'>) this.GetWidthGetterPrefix( ): None this.IsDepthSet( ): True this.GetDepth( ): 9 this.GetDepthRaw( ): 9 this.GetDepthDefaultValue( ): 2 this.GetDepthLen( ): 1 this.HasDepthGetterPrefix( ): None this.GetDepthGetterPrefix( ): (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) this.IsHeightSet( ): True this.GetHeight( ): 9 this.GetHeightRaw( ): 9 this.GetHeightDefaultValue( ): 0 this.GetHeightLen( ): 1 this.HasHeightGetterPrefix( ): <class 'int'> this.GetHeightGetterPrefix( ): (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) _foo --- MyClass: ---- id( this.__class__ ): 2231452349064 :::: id( this ): 2231448475016 Key Getter Value | Raw Key Raw / Stored Value | Get Default Value Default Value | Get Allowed Types Allowed Types | Get Allowed Values Allowed Values | Name: _foo | _Name: _foo | __Name.DefaultValue( ): AccessorFuncDemoClass | __Name.GetAllowedTypes( ) <class 'str'> | __Name.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type | x: 10 | _x: 10 | __x.DefaultValue( ): 1111 | __x.GetAllowedTypes( ) (<class 'int'>, <class 'float'>) | __x.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type | y: 10 | _y: 10 | __y.DefaultValue( ): 2222 | __y.GetAllowedTypes( ) (<class 'int'>, <class 'float'>) | __y.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type | z: 10 | _z: 10 | __z.DefaultValue( ): 3333 | __z.GetAllowedTypes( ) (<class 'int'>, <class 'float'>) | __z.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type | Blah: string Blah | _Blah: string Blah | __Blah.DefaultValue( ): <class 'int'> | __Blah.GetAllowedTypes( ) <class 'str'> | __Blah.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type | Width: False | _Width: False | __Width.DefaultValue( ): 1 | __Width.GetAllowedTypes( ) (<class 'int'>, <class 'bool'>) | __Width.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type | Height: 9 | _Height: 9 | __Height.DefaultValue( ): 0 | __Height.GetAllowedTypes( ) <class 'int'> | __Height.GetAllowedValues( ) (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) | Depth: 9 | _Depth: 9 | __Depth.DefaultValue( ): 2 | __Depth.GetAllowedTypes( ) Saved Value Restricted to Authorized Values ONLY | __Depth.GetAllowedValues( ) (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) |
Обратите внимание, что из-за ограниченных типов данных или ограничений значений некоторые данные не были назначены - это сделано специально. Сеттер запрещает присваивать неверные типы данных или значения, даже если они заданы как значения по умолчанию ( если только вы не переопределить поведение защиты по умолчанию)
Код не был опубликован здесь, потому что у меня не было места после примеров и объяснений... И еще потому, что она изменится.
Пожалуйста, обратите внимание: во время этой публикации файл грязный - это изменится. Но, если вы запустите его в Sublime Text и скомпилируете его, или запустите его из Python, он скомпилирует и выплюнет тонну информации-часть AccessorDB не будет выполнена ( которая будет использоваться для обновления принт-геттеров и Вспомогательные функции GetKeyOutput наряду с изменением на функцию экземпляра, вероятно, помещаются в одну функцию и переименовываются-ищите ее.. )
Далее: не все, что требуется для его запуска - много комментируемого материала внизу для дополнительной информации, используемой для отладки - его может не быть там, когда вы загружаете его. Если это так, вы должны иметь возможность раскомментировать и перекомпилировать, чтобы получить дополнительную информацию.
Я ищу обходной путь к необходимости MyClassBase: pass, MyClass( MyClassBase ): ... - если вы знаете решение-опубликуйте его.
Единственное, что необходимо в классе, это _ _ lines - str предназначен для отладки, как и init - они могут быть удалены из демонстрационного класса, но вам нужно будет закомментировать или удалить некоторые из строк ниже ( _foo / 2 / 3)..
Классы String, Dict и Util вверху являются частью моей библиотеки Python - они не являются полными. Я скопировал несколько вещей, которые мне были нужны в библиотеке, и я создал несколько новых. Полный код будет связан с полной библиотекой и будет включать ее вместе с предоставлением обновленных вызовов и удалением кода (фактически, единственным кодом останется демонстрационный класс и операторы печати - система AccessorFunc будет перемещена в библиотеку )...
Часть файла:
## ## MyClass Test AccessorFunc Implementation for Dynamic 1-line Parameters ## class AccessorFuncDemoClassBase( ): pass class AccessorFuncDemoClass( AccessorFuncDemoClassBase ): __Name = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'Name', default = 'AccessorFuncDemoClass', allowed_types = ( TYPE_STRING ), allowed_values = VALUE_ANY, documentation = 'Name Docs', getter_prefix = 'Get', key = 'Name', allow_erroneous_default = False, options = { } ) __x = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'X', default = 1111, allowed_types = ( TYPE_INTEGER, TYPE_FLOAT ), allowed_values = VALUE_ANY, documentation = 'X Docs', getter_prefix = 'Get', key = 'x', allow_erroneous_default = False, options = { } ) __Height = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'Height', default = 0, allowed_types = TYPE_INTEGER, allowed_values = VALUE_SINGLE_DIGITS, documentation = 'Height Docs', getter_prefix = 'Get', key = 'Height', allow_erroneous_default = False, options = { } )
Эта красота делает невероятно легким создание новых классов с динамически добавляемыми свойствами с помощью функций доступа / обратных вызовов / принудительного применения типа данных / значения, и т.д.
На данный момент ссылка находится по адресу ( эта ссылка должна отражать изменения в документе. ): https://www.dropbox.com/s/6gzi44i7dh58v61/dynamic_properties_accessorfuncs_and_more.py?dl=0
Также: если вы не используете Sublime Text, я рекомендую его по сравнению с Notepad++, Atom, Visual Code и другими из-за правильных реализаций потоков, что делает его намного более быстрым в использовании... Я также работаю над IDE-подобной системой отображения кода для него - взгляните на: https://bitbucket.org/Acecool/acecoolcodemappingsystem/src/master/ (Сначала добавьте Repo в менеджер пакетов, а затем установите плагин - когда версия 1.0.0 будет готова, я добавлю его в основной список плагинов... )
Я надеюсь, что это решение поможет... и, как всегда:
Просто потому, что это работает, не делает его правильным-Джош 'Acecool' Мозер
Единственный способ динамически присоединить свойство-это создать новый класс и его экземпляр с вашим новым свойством.
class Holder: p = property(lambda x: vs[i], self.fn_readonly) setattr(self, k, Holder().p)
Недавно я столкнулся с подобной проблемой, решение, которое я придумал, использует
__getattr__
и__setattr__
для свойств, которые я хочу обработать, все остальное передается оригиналам.class C(object): def __init__(self, properties): self.existing = "Still Here" self.properties = properties def __getattr__(self, name): if "properties" in self.__dict__ and name in self.properties: return self.properties[name] # Or call a function, etc return self.__dict__[name] def __setattr__(self, name, value): if "properties" in self.__dict__ and name in self.properties: self.properties[name] = value else: self.__dict__[name] = value if __name__ == "__main__": my_properties = {'a':1, 'b':2, 'c':3} c = C(my_properties) assert c.a == 1 assert c.existing == "Still Here" c.b = 10 assert c.properties['b'] == 10