Как добавить свойство в класс динамически?


Цель состоит в том, чтобы создать макет класса, который ведет себя как результирующий набор БД.

Так, например, если запрос базы данных возвращает, используя выражение 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 155

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

Как динамически добавить свойство в класс python?

Предположим, у вас есть объект, к которому вы хотите добавить свойство. Обычно я хочу использовать свойства, когда мне нужно начать управлять доступом к атрибуту в коде, который имеет нисходящее использование, чтобы я мог поддерживать согласованный API. Теперь я обычно добавляю их в исходный код, где определен объект, но предположим, что у вас нет такого доступа, или вам нужно действительно динамически выбирать свои функции программно.

Создайте класс

Используя пример, основанный на документации для 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