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

216

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

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

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

PS Мне известна альтернатива, представленная в разделе Как используется __getattribute__метод?

Энтони Конг
источник
2
В вашем коде есть несколько опечаток: для определения fn_readonly нужны a :и __init__ссылки self.fn_readyonly.
Mhawke
Ты прав. Я добавил эту функцию установки в последнюю минуту, чтобы подчеркнуть причину создания свойства во время выполнения.
Энтони Конг
Основная проблема, возникшая у меня при создании свойств при инициализации, заключалась в том, что в некоторых случаях, если я быстро вызвал помощников или возникла проблема, я получал сообщение об ошибке, что их не было, несмотря на то, что они есть. В моем решении ниже, я создаю 2 класса. Один как Base / Parent (которого я пытаюсь найти решение, чтобы избежать), и основной объект, который расширяет Base / Parent. Затем в главном объекте, без инициализации, я вызываю моего создателя AccessorFunc, который создает свойства, вспомогательные функции и многое другое.
Acecool
т.е.: Class ExampleBase: pass; class Example (ExampleBase): __x = Accessor (ExampleBase, 'x', 'X', 123); --- который создаст свойство для x и именованных функций, используя X, поэтому GetX, SetX и другие ... и .x, ._x и .__ x для свойства. Таким образом, .x - это само свойство данных, через которые они проходят (получение / установка через self.x = 123; или self.x для вывода). Я использовал self._x для сохраненных данных RAW, чтобы они были легко доступны, так как я также позволял назначать значения по умолчанию, не устанавливая их в сохраненных данных. поэтому _x может быть None, а .x может вернуть 123. и .__ x связан с Accessor
Acecool
Вот ссылка на базовую версию, которая создает динамические свойства и динамические функции - файл имеет кучу ссылок на другие версии. Одним из них является система AccessorFunc, использующая функцию для создания помощников (одна для функций, одна для свойств, одна для обоих в качестве отдельных элементов - поэтому она не использует сокращение кода в этом файле). Если чего-то не хватает, один из другие файлы имеют это: dropbox.com/s/phnnuavssmzeqrr/dynamic_properties_simple.py?dl=0
Acecool

Ответы:

334

Полагаю, мне следует расширить этот ответ, теперь, когда я стал старше и мудрее и знаю, что происходит. Лучше поздно, чем никогда.

Вы можете добавить свойство к классу динамически. Но в этом-то и суть: вы должны добавить это в класс .

>>> class Foo(object):
...     pass
... 
>>> foo = Foo()
>>> foo.a = 3
>>> Foo.b = property(lambda self: self.a + 1)
>>> foo.b
4

На 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__; для дескрипторов идиоматично возвращать себя, когда к ним обращаются как к атрибутам класса.

Я думаю, это очень круто, что практически вся ОО-система Python может быть выражена в Python. :)

О, и я недавно написал многословное сообщение в блоге о дескрипторах , если вам интересно.

Eevee
источник
35
Нет необходимости добавлять метод add_property. setattr (Foo, 'name', property (func))
Кортни Д
8
Ваше "Но в этом и есть подвох ..." просто спасло меня от нескольких часов работы. Спасибо.
Мэтт Хауэлл
2
Если вы хотите определить свойство для одного экземпляра, вы можете создать класс во время выполнения и изменить __class__ .
Уилфред Хьюз
1
как насчет @ myproperty.setter? Как добавить это динамически?
LRMAAX
Вам не нужно добавлять свойство в инициализированный объект. Это может означать, что это касается только экземпляра, но мне придется перепроверить это. Я знаю, что столкнулся с аналогичной проблемой, когда мои динамические свойства были только экземпляром, я также получил статическую настройку и ту, которую я хотел, которая относится к объекту, чтобы будущие инициализации использовали их. Мой пост ниже, и он создает вспомогательные функции и простые способы легкого доступа ко всему. .x для свойства, ._x для необработанных данных, которые использует получатель / установщик (который может быть Нет), и .__ x для объекта доступа.
Acecool
57

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

Итак, что вы хотите - это словарь, в котором вы можете написать a ['b'] как ab?

Это легко:

class atdict(dict):
    __getattr__= dict.__getitem__
    __setattr__= dict.__setitem__
    __delattr__= dict.__delitem__
bobince
источник
1
в более общем плане это служит ограниченным целям. если диктет имеет многоуровневую иерархию, например, d = {'a1': {'b': 'c'}, 'a2': ...}, то, хотя вы можете делать d.a1 или d.a2, вы можете ' t do d.a1.b
Shreyas
1
Одна вещь , чтобы иметь в виду, что это позволяет устанавливать значения атрибутов для атрибутов с таким же именем , как и методы Dict или атрибутов, но не позволяет извлечения значения таким же образом снова: d.items = 1, d.itemsвозвращается <built-in method items of atdict object at ...>. Вы все еще можете сделать d["items"]или использовать __getattribute__вместо __getattr__, но это мешает использовать большинство методов dict.
Marcono1234
Просто используйте библиотеку Munch ! (разветвленная связка)
Брайан Петерсон
38

Кажется, вы могли бы решить эту проблему намного проще с помощью a 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()не работает на экземплярах.

Eevee
источник
Отличная идея. К сожалению, я застрял с Python 2.4 на данный момент.
Энтони Конг
2
Парень, который написал, namedtupleзаслуживает награды за то, что он сделал его гладким и элегантным, чтобы быть верным объектно-ориентированным принципам.
Кит Пинсон
4
Извините, в лучшем случае этот ответ применим только к особому случаю, когда один требуемый класс, состоящий только из атрибутов только для чтения, все заранее знают. Другими словами, я не думаю, что это затрагивает более широкий вопрос о том, как добавить общие свойства - не только для чтения - к классу во время выполнения (равно как и текущая версия другого дополнения). размещено автором).
Мартино
@martineau так ... передать больше аргументов property()? ни в одном ответе нет ничего специфичного только для чтения.
Eevee
32

Вам не нужно использовать свойство для этого. Просто переопределите, __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!
Райан
источник
9

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

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

Создать класс

Используя пример, основанный на документации дляproperty , давайте создадим класс объекта со скрытым атрибутом и создадим его экземпляр:

class C(object):
    '''basic class'''
    _x = None

o = C()

В Python мы ожидаем, что есть один очевидный способ сделать что-то. Однако в этом случае я собираюсь показать два способа: с обозначением декоратора и без. Во-первых, без обозначения декоратора. Это может быть более полезно для динамического назначения методов получения, установки или удаления.

Динамический (он же 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
Аарон Холл
источник
5

Я задал похожий вопрос в этом сообщении о переполнении стека, чтобы создать фабрику классов, которая создавала простые типы. Результатом стал ответ, который имел рабочую версию фабрики классов. Вот фрагмент ответа:

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)
>>>

Вы можете использовать некоторые варианты этого для создания значений по умолчанию, которые являются вашей целью (в этом вопросе также есть ответ, который имеет дело с этим).

kjfletch
источник
4

Не уверен, что я полностью понимаю вопрос, но вы можете изменить свойства экземпляра во время выполнения с помощью встроенного в __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
Свежий полумесяц
источник
По сути, мой вопрос заключается в том, чтобы выяснить, возможно ли создать новое свойство во время выполнения. Консенсус кажется отрицательным. Ваше предложение, конечно, просто и практично. (То же самое относится и к другим ответам, использующим dict )
Энтони Конг
Простой ответ также будет:self.__dict__[key] = value
Аллан Карлсон
4

Для тех, кто прибывает из поисковых систем, вот две вещи, которые я искал, говоря о динамических свойствах:

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 полезна для упрощения доступа к данным, хранящимся в классе (как в примере выше).

Если вам нужно только одно динамическое свойство, взгляните на встроенную функцию property () .

tleb
источник
4

Вы не можете добавить новый property()экземпляр во время выполнения, потому что свойства являются дескрипторами данных. Вместо этого вы должны динамически создать новый класс или перегрузить __getattribute__для обработки дескрипторов данных в экземплярах.

Алекс Гейнор
источник
Это не верно. Вы можете добавить свойство в класс, а затем получить к нему доступ из метода.
Ахмед
2

Лучший способ достичь - это определить __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'

nosklo
источник
2

Еще один пример того, как добиться желаемого эффекта

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
lehins
источник
2

Вот решение, которое:

  • Позволяет указывать имена свойств в виде строк , чтобы они могли поступать из какого-либо внешнего источника данных, а не быть перечислены в вашей программе.
  • Добавляет свойства при определении класса , а не каждый раз, когда создается объект.

После того, как класс был определен, вы просто делаете это для динамического добавления свойства:

setattr(SomeClass, 'propertyName', property(getter, setter))

Вот полный пример, протестированный в Python 3:

#!/usr/bin/env python3

class Foo():
  pass

def get_x(self):
  return 3

def set_x(self, value):
  print("set x on %s to %d" % (self, value))

setattr(Foo, 'x', property(get_x, set_x))

foo1 = Foo()
foo1.x = 12
print(foo1.x)
Дэвид Грейсон
источник
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
Энтони Холломан
источник
1

Это немного отличается от того, что хотел ОП, но я ломал голову, пока не нашел рабочее решение, поэтому я выкладываю сюда для следующего парня / гал

Мне нужен был способ указать динамические сеттеры и геттеры.

class X:
    def __init__(self, a=0, b=0, c=0):
        self.a = a
        self.b = b
        self.c = c

    @classmethod
    def _make_properties(cls, field_name, inc):
        _inc = inc

        def _get_properties(self):
            if not hasattr(self, '_%s_inc' % field_name):
                setattr(self, '_%s_inc' % field_name, _inc)
                inc = _inc
            else:
                inc = getattr(self, '_%s_inc' % field_name)

            return getattr(self, field_name) + inc

        def _set_properties(self, value):
            setattr(self, '_%s_inc' % field_name, value)

        return property(_get_properties, _set_properties)

Я знаю свои поля раньше времени, поэтому я собираюсь создать свои свойства. ПРИМЕЧАНИЕ: вы не можете сделать этот экземпляр PER, эти свойства будут существовать в классе !!!

for inc, field in enumerate(['a', 'b', 'c']):
    setattr(X, '%s_summed' % field, X._make_properties(field, inc))

Давайте проверим все это сейчас ..

x = X()
assert x.a == 0
assert x.b == 0
assert x.c == 0

assert x.a_summed == 0  # enumerate() set inc to 0 + 0 = 0
assert x.b_summed == 1  # enumerate() set inc to 1 + 0 = 1
assert x.c_summed == 2  # enumerate() set inc to 2 + 0 = 2

# we set the variables to something
x.a = 1
x.b = 2
x.c = 3

assert x.a_summed == 1  # enumerate() set inc to 0 + 1 = 1
assert x.b_summed == 3  # enumerate() set inc to 1 + 2 = 3
assert x.c_summed == 5  # enumerate() set inc to 2 + 3 = 5

# we're changing the inc now
x.a_summed = 1 
x.b_summed = 3 
x.c_summed = 5

assert x.a_summed == 2  # we set inc to 1 + the property was 1 = 2
assert x.b_summed == 5  # we set inc to 3 + the property was 2 = 5
assert x.c_summed == 8  # we set inc to 5 + the property was 3 = 8

Это сбивает с толку? Да, извините, я не смог придумать сколько-нибудь значимых примеров из реального мира. Кроме того, это не для слабонервных.

Хавьер Буззи
источник
Если я правильно помню, я нашел способ в течение всего моего тестирования создать свойство типа STATIC / динамически добавленный g / setter. Я должен был пройти через все мои предыдущие - но возможность добавить что-то, что разделено между всеми экземплярами, определенно возможно. Что касается создания в процессе для экземпляра ... Я почти уверен, что вы можете сделать так, чтобы у одного экземпляра было то, чего нет у другого. Я должен был проверить, но я столкнулся с чем-то вроде этого (в моих первых попытках я допустил ошибку, из-за которой были созданы функции, но не во всех случаях они были из-за недостатка)
Acecool
Также приветствуется любое возможное решение, так как это репо знаний. Также интересно видеть, как разные люди создают решения проблемы. Мое решение делает много, вы сократили это до чего-то более простого, чтобы поделиться. Я также сделал свой вариант меньшего размера - он должен быть где-то в этой теме - и я просто понял, что это не тот, который я написал: -) ...
Acecool
0

Это похоже на работу (но смотри ниже):

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__)
Дэвид Х
источник
0

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

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

Так, например, если запрос к базе данных возвращается, используя выражение dict {'ab':100, 'cd':200}, то я хотел бы увидеть

>>> dummy.ab
100

Я продемонстрирую, как использовать модуль namedtuplefrom 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

Аарон Холл
источник
0
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
Сергей Хачко
источник
0

Расширяя идею от kjfletch

# This is my humble contribution, extending the idea to serialize
# data from and to tuples, comparison operations and allowing functions
# as default values.

def Struct(*args, **kwargs):
    FUNCTIONS = (types.BuiltinFunctionType, types.BuiltinMethodType, \
                 types.FunctionType, types.MethodType)
    def init(self, *iargs, **ikwargs):
        """Asume that unamed args are placed in the same order than
        astuple() yields (currently alphabetic order)
        """
        kw = list(self.__slots__)

        # set the unnamed args
        for i in range(len(iargs)):
            k = kw.pop(0)
            setattr(self, k, iargs[i])

        # set the named args
        for k, v in ikwargs.items():
            setattr(self, k, v)
            kw.remove(k)

        # set default values
        for k in kw:
            v = kwargs[k]
            if isinstance(v, FUNCTIONS):
                v = v()
            setattr(self, k, v)

    def astuple(self):
        return tuple([getattr(self, k) for k in self.__slots__])

    def __str__(self):
        data = ['{}={}'.format(k, getattr(self, k)) for k in self.__slots__]
        return '<{}: {}>'.format(self.__class__.__name__, ', '.join(data))

    def __repr__(self):
        return str(self)

    def __eq__(self, other):
        return self.astuple() == other.astuple()

    name = kwargs.pop("__name__", "MyStruct")
    slots = list(args)
    slots.extend(kwargs.keys())
    # set non-specific default values to None
    kwargs.update(dict((k, None) for k in args))

    return type(name, (object,), {
        '__init__': init,
        '__slots__': tuple(slots),
        'astuple': astuple,
        '__str__': __str__,
        '__repr__': __repr__,
        '__eq__': __eq__,
    })


Event = Struct('user', 'cmd', \
               'arg1', 'arg2',  \
               date=time.time, \
               __name__='Event')

aa = Event('pepe', 77)
print(aa)
raw = aa.astuple()

bb = Event(*raw)
print(bb)

if aa == bb:
    print('Are equals')

cc = Event(cmd='foo')
print(cc)

Вывод:

<Event: user=pepe, cmd=77, arg1=None, arg2=None, date=1550051398.3651814>
<Event: user=pepe, cmd=77, arg1=None, arg2=None, date=1550051398.3651814>
Are equals
<Event: user=None, cmd=foo, arg1=None, arg2=None, date=1550051403.7938335>
Астерио Гонсалес
источник
0

Хотя дано много ответов, я не смог найти тот, которым я доволен. Я выяснил свое собственное решение, которое делает propertyработу для динамического случая. Источник для ответа на оригинальный вопрос:

#!/usr/local/bin/python3

INITS = { 'ab': 100, 'cd': 200 }

class DP(dict):
  def __init__(self):
    super().__init__()
    for k,v in INITS.items():
        self[k] = v 

def _dict_set(dp, key, value):
  dp[key] = value

for item in INITS.keys():
  setattr(
    DP,
    item,
    lambda key: property(
      lambda self: self[key], lambda self, value: _dict_set(self, key, value)
    )(item)
  )

a = DP()
print(a)  # {'ab': 100, 'cd': 200}
a.ab = 'ab100'
a.cd = False
print(a.ab, a.cd) # ab100 False
Роб Л
источник
0

Что-то, что работает для меня, это:

class C:
    def __init__(self):
        self._x=None

    def g(self):
        return self._x

    def s(self, x):
        self._x = x

    def d(self):
        del self._x

    def s2(self,x):
        self._x=x+x

    x=property(g,s,d)


c = C()
c.x="a"
print(c.x)

C.x=property(C.g, C.s2)
C.x=C.x.deleter(C.d)
c2 = C()
c2.x="a"
print(c2.x)

Вывод

a
aa
прости
источник
-1

Недавно я столкнулся с аналогичной проблемой, решение, которое я нашел, использует __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
teeks99
источник
Я изучил это, однако вы технически просматриваете список в своих помощниках по получению и установке. Из-за этого каждый вызов будет медленнее, потому что вы сначала просматриваете его из списка, а не обращаетесь к нему напрямую. Если Python не отобразит это автоматически для вас; может, но я еще не проверил это, чтобы знать наверняка, но я беспокоюсь, прежде чем попробовать. Во-вторых, делая это, вы должны определить помощников по-другому. Вы также не можете заблокировать типы данных и / или значения, не заканчивая большим словарем или большим количеством дополнительных строк.
Acecool
то есть: я либо должен создать базовый класс, из которого я расширяю всех своих детей, использующих систему, либо я должен добавить магические функции s / getattr ко всему и дублировать систему каждый раз. Объявление свойств также означает, что вы должны настроить их одним способом, и если вы хотите какую-либо дополнительную поддержку, как я перечислил, такую ​​как защита типа данных или значения, чтобы разрешить или запретить назначение данных и другие помощники затем вы должны их кодировать. Конечно, вы можете сделать систему похожей по поведению, но в итоге вы объявите ее немного иначе и громоздче.
Acecool
-1

Вот простой пример для создания объекта свойства программно.

#!/usr/bin/python3

class Counter:
    def __init__(self):
        cls = self.__class__
        self._count = 0
        cls.count = self.count_ref()

    def count_get(self):
        print(f'count_get: {self._count}')
        return self._count

    def count_set(self, value):
        self._count = value
        print(f'count_set: {self._count}')

    def count_del(self):
        print(f'count_del: {self._count}')

    def count_ref(self):
        cls = self.__class__
        return property(fget=cls.count_get, fset=cls.count_set, fdel=cls.count_del)

counter = Counter()

counter.count
for i in range(5):
    counter.count = i
del counter.count

'''
output
======
count_get: 0
count_set: 0
count_set: 1
count_set: 2
count_set: 3
count_set: 4
count_del: 4
'''
Сухас Сидду
источник
-2

Единственный способ динамически присоединить свойство - это создать новый класс и его экземпляр с вашим новым свойством.

class Holder: p = property(lambda x: vs[i], self.fn_readonly)
setattr(self, k, Holder().p)
М.УТКУ АЛЬТИНКАЯ
источник
1
это не похоже на работу. он назначит результат свойства, а не само свойство.
Маялдай
Это неверно Я присоединяю динамические свойства к моей системе, даже не инициализируя класс. Инициализируйте как x = Example (), затем добавьте свойство к x.
Acecool
Если вы посмотрите на мой код, вы увидите, что я использую класс ExampleBase: pass, затем класс Example (ExampleBase): ... затем я присоединяю свойства к ExampleBase, потому что имя тогда существует, и так как Example выходит из него, у него есть доступ ко всему. Я использую __ var, чтобы вспомогательный метод доступа мог иметь прямой доступ к объектам доступа, я использую _ для хранимых (необработанных) данных, которые могут иметь значение None, и не подчеркивать фактическое свойство, которое проходит через метод получения. Я могу вызвать функцию получения, используя динамически добавленную функцию, или использовать свойство. Все без первого init'd.
Acecool
Примечание: я упоминал об этом - но мое определение для определения означает, что ссылка существует в пространстве имен - то есть: class Example (Object): pass ... Он существует, но не был инициализирован. Инициализация означает бла = Пример (); теперь объект был «продублирован» и сконструирован, а затем сохранен как ссылка в бла. --- Если вы сделаете это, то динамически добавленные функции / свойства должны быть только в экземпляре - проблема, с которой я столкнулся, заключалась в том, что даже если функции существовали, были случаи, когда я получал ошибку, говоря, что их нет. Либо ошибка блокировки остановила создание, либо асинхронное выполнение.
Acecool
Несколько других замечаний: можно создавать свойства динамически и функционировать так, чтобы они существовали только для каждого экземпляра. Вы также можете сделать их так, чтобы они существовали для объекта (это то, что вы хотели бы в большинстве случаев). И в некоторых случаях добавленные элементы являются «статическими», то есть одни и те же ссылки, а возвращаемые значения являются общими для всех экземпляров - если вы обновляете в одной области, все получают одинаковые ..
Acecool
-6

Многие из предоставленных ответов требуют так много строк для каждого свойства, то есть / и / или - что я бы назвал уродливой или утомительной реализацией из-за повторяемости, требуемой для нескольких свойств и т. Д. Я предпочитаю сводить детали вниз / упрощать их, пока они больше не может быть упрощено или пока это не принесет особой цели.

Вкратце: в завершенных работах, если я повторяю 2 строки кода, я обычно преобразую его в вспомогательную функцию в одну строку и так далее ... Я упрощаю математические или нечетные аргументы, такие как (start_x, start_y, end_x, end_y), до (x, y, w, h) то есть x, y, x + w, y + h (иногда требуется min / max или если w / h отрицательны и реализации не нравится, я вычту из x / у и пресс с ч / ч и т. д.).

Переопределение внутренних методов получения / установки является нормальным способом, но проблема в том, что вы должны сделать это для каждого класса или создать родительский класс для этой базы ... Это не работает для меня, поскольку я предпочел бы свободно выбирать детей / родителей для наследования, дочерних узлов и т. д.

Я создал решение, которое отвечает на вопрос, не используя тип данных Dict для предоставления данных, так как считаю, что ввод данных является утомительным и т. Д.

Мое решение требует, чтобы вы добавили 2 дополнительные строки над вашим классом, чтобы создать базовый класс для класса, в который вы хотите добавить свойства, затем 1 строку на каждый, и у вас есть возможность добавить обратные вызовы для управления данными, сообщать вам об изменениях данных ограничить данные, которые могут быть установлены на основе значения и / или типа данных, и многое другое.

У вас также есть возможность использовать _object.x, _object.x = значение, _object.GetX (), _object.SetX (значение), и они обрабатываются эквивалентно.

Кроме того, значения являются единственными нестатическими данными, которые назначаются экземпляру класса, но фактическое свойство присваивается классу, то есть вещи, которые вы не хотите повторять, повторять не нужно ... Вы может назначить значение по умолчанию, чтобы метод получения не нуждался в нем каждый раз, хотя есть опция для переопределения значения по умолчанию по умолчанию, и есть другая опция, чтобы метод получения возвращал необработанное сохраненное значение путем переопределения возвратов по умолчанию (примечание: этот метод означает, что необработанное значение присваивается только при назначении значения, в противном случае оно равно None - когда значением является Reset, оно присваивает None и т. д.)

Также есть много вспомогательных функций - первое добавленное свойство добавляет в класс примерно 2 помощника для ссылки на значения экземпляра ... Это повторяемые переменные ResetAccessors (_key, ..) (все можно повторить, используя первые именованные аргументы ) и SetAccessors (_key, _value) с возможностью добавления большего числа в основной класс для повышения эффективности - планируется: способ группировать средства доступа вместе, так что если вы склонны сбрасывать несколько одновременно, каждый раз Вы можете назначить их группе и сбросить группу вместо повторения именованных ключей каждый раз, и многое другое.

Сохраненное значение экземпляра / raw хранится в классе. , класс. ссылается на класс Accessor, который содержит статические переменные / значения / функции для свойства. _класс. это свойство, которое вызывается при доступе через класс экземпляра во время установки / получения и т. д.

Accessor _class .__ указывает на класс, но, поскольку он является внутренним, его необходимо назначить в классе, поэтому я решил использовать __Name = AccessorFunc (...) для его присвоения, по одной строке на свойство со многими необязательными аргументы для использования (используя ключевые переменные, потому что их проще и эффективнее идентифицировать и поддерживать) ...

Как я уже упоминал, я также создаю много функций, некоторые из которых используют информацию о функциях доступа, поэтому их не нужно вызывать (поскольку в данный момент это немного неудобно - сейчас вам нужно использовать _class. .FunctionName (_class_instance) , args) - я нашел способ использовать стек / трассировку для получения ссылки на экземпляр, чтобы получить значение, добавив функции, которые либо запускают этот битовый марафон, либо добавив методы доступа к объекту и используя self (названный this, чтобы указать, что они для экземпляра и чтобы сохранить доступ к себе, ссылку на класс AccessorFunc и другую информацию в определениях функций).

Это не совсем сделано, но это фантастическая нога. Примечание. Если вы не используете __Name = AccessorFunc (...) для создания свойств, у вас не будет доступа к клавише __, даже если я определю ее в функции init. Если вы делаете, то нет никаких проблем.

Также: Обратите внимание, что Имя и Ключ разные ... Имя является «формальным», используется при создании имени функции, а ключ предназначен для хранения и доступа к данным. то есть _class.x, где строчная буква x - это ключ, имя будет прописной буквой X, так что GetX () - это функция вместо Getx (), которая выглядит немного странно. это позволяет self.x работать и выглядеть соответствующим образом, но также позволяет GetX () и выглядеть соответствующим образом.

У меня есть пример класса с ключом / именем, идентичным, и другим, чтобы показать. Для вывода данных создано множество вспомогательных функций (Примечание: не все это завершено), чтобы вы могли видеть, что происходит.

Текущий список функций, использующих key: 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 не сделана (которая будет использоваться для обновления Print Getters и помощника GetKeyOutput функции вместе с изменением на функцию Instance, возможно, помещенную в одну функцию и переименованную - ищите ее ..)

Далее: не все требуется для его запуска - большая часть комментариев внизу предназначена для дополнительной информации, используемой для отладки - ее может не быть при загрузке. Если это так, вы сможете раскомментировать и перекомпилировать, чтобы получить больше информации.

Я ищу обходной путь к необходимости MyClassBase: pass, MyClass (MyClassBase): ... - если вы знаете о решении - опубликуйте его.

Единственное, что необходимо в классе - это __-строки - 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 = { } )

Эта красота делает невероятно простым создание новых классов с динамически добавленными свойствами с помощью AccessorFuncs / callbacks / data-type / value принудительного применения и т. Д.

На данный момент ссылка на (Эта ссылка должна отражать изменения в документе.): 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/ (сначала добавьте репо в диспетчере пакетов, затем установите плагин - когда версия 1.0.0 будет готова, я добавлю это к основному списку плагинов ...)

Я надеюсь, что это решение поможет ... и, как всегда:

Просто потому, что это работает, не делает это правильно - Джош 'Acecool' Мозер

Acecool
источник
Я хотел добавить быстрое отображение того, как выглядел класс, так что вам не нужно открывать файл кода, но комментарии не поддерживают его ...
Acecool
По-видимому, это вызывает много ненависти, что сбивает с толку. Он делает именно то, что запрашивает OP - динамическое добавление свойств к объекту. Он также добавляет вспомогательные функции, которые не должны быть включены - возможно, именно поэтому он становится ненавистным - и он также обеспечивает разработчику простой способ доступа к свойству (.x), которое обрабатывается через метод получения, хранится необработанное значение (._x), которое может быть None, когда .x возвращает значение по умолчанию или что-то еще, и способ доступа к аксессору для использования помощников, изменения вещей и т. д. (.__ x) ....
Acecool