Як додати властивість до класу динамічно?


215

Мета - створити макет-клас, який веде себе як набір результатів 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 потребує а :та __init__посилання self.fn_readyonly.
mhawke

Ти правий. Я додав цю функцію сеттера в останню хвилину, щоб підкреслити причину створення властивості під час виконання.
Ентоні Конг

Основна проблема у створенні властивостей при ініціалізації полягала в тому, що в деяких випадках, якщо я швидко зателефонував до помічників, або виникла проблема, я отримаю помилку, що їх не було, незважаючи на те, що вони є. У своєму рішенні нижче я створюю 2 класи. Один як База / Батько (що я намагаюся знайти рішення, щоб уникнути), і основний об'єкт, який розширює Базу / Батько. Потім, в основному об'єкті, не ініціалізуючи, я називаю свого творця AccessorFunc, який створює властивості, допоміжні функції тощо.
Acecool

тобто: клас ExampleBase: пас; Приклад класу (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, пов’язані з
Аксесором

Ось посилання на основну версію, яка створює динамічні властивості, та динамічні функції - файл має купу посилань на інші версії. Один - це система AccessorFunc, яка використовує функцію для створення помічників (одна для функцій, одна для властивостей, одна для обох як окремих елементів - тому вона не використовує скорочення коду ні в чому у цьому файлі) .. Якщо чогось не вистачає, один із в інших файлах є: dropbox.com/s/phnnuavssmzeqrr/dynamic_properties_simple.py?dl=0
Acecool

Відповіді:


333

Я думаю, я повинен розширити цю відповідь, тепер, коли я старший і мудріший і знаю, що відбувається. Краще пізно, ніж ніколи.

Ви можете додати властивість до класу динамічно. Але в цьому і є улов: ви повинні додати його до класу .

>>> 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 викрити сантехніку всієї його реалізації. Насправді, існує ще один тип дескриптора, навіть більш поширений, ніж 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. :)

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


35
Не потрібно додавати метод add_property. setattr (Foo, 'name', property (func))
Кортні Д

8
Ваше "Але ось улов ..." просто врятувало мене кілька годин роботи. Дякую.
Метт Хоуелл

2
Якщо ви хочете визначити властивість в одному екземплярі, ви можете створити клас під час виконання та змінити __class__ .
Вільфред Х'юз

1
як щодо @ myproperty.setter? Як його додати динамічно?
LRMAAX

Не потрібно додавати властивість до ініціалізованого об’єкта. Це може означати, що він прилягає лише до екземпляра, але мені доведеться ще раз перевірити це. Я знаю, що я зіткнувся з подібною проблемою, де мої динамічні властивості були лише екземпляром, я також закінчив статичну установку і ту, яку я хотів, що є об'єктом, щоб майбутні ініціалізації використовуватимуть їх. Мій пост внизу, і він створює допоміжні функції та прості способи легко отримати доступ до всього. .x для властивості, ._x для необроблених даних, які використовує геттер / сеттер (які можуть бути "Ніхто"), і .__ x для об'єкта "accessor".
Acecool

57

Мета - створити макет-клас, який веде себе як набір результатів db.

Отже, що ви хочете, це словник, де ви можете написати a ['b'] як ab?

Це просто:

class atdict(dict):
    __getattr__= dict.__getitem__
    __setattr__= dict.__setitem__
    __delattr__= dict.__delitem__

1
в більш загальних умовах це служить обмеженій меті. якщо диктант має багаторівневу ієрархію, наприклад d = {'a1': {'b': 'c'}, 'a2': ...}, тоді як ви можете робити d.a1 або d.a2, ви можете ' т зробити d.a1.b
SHREYAS

1
Одна річ , щоб мати на увазі, що це дозволяє встановлювати значення атрибутів для атрибутів з таким же ім'ям , як і методи Dict або атрибутів, але не дозволяє вилучення значення таким же чином знову: d.items = 1, d.itemsповертається <built-in method items of atdict object at ...>. Ви все ще можете робити d["items"]або використовувати __getattribute__замість цього __getattr__, але це заважає використовувати більшість методів диктату.
Marcono1234

Просто використовуйте бібліотеку Munch ! (fork of bunch)
Брайан Петерсон

38

Здається, ви могли б вирішити цю проблему набагато простіше 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()не працює в екземплярах.


Чудова ідея. На жаль, на даний момент я застряг у 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 ми очікуємо, що існує один очевидний спосіб робити речі. Однак у цьому випадку я збираюся показати два способи: із позначенням декоратора та без. По-перше, без позначень декораторів. Це може бути кориснішим для динамічного призначення геттерів, сетерів або делетерів.

Динамічний (він же Мавпа Патчінг)

Давайте створимо для нашого класу:

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

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


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

По суті, моє питання полягає у з'ясуванні, чи можна створити нову властивість під час виконання. Здається, консенсус є негативним. Ваша пропозиція, безумовно, проста і практична. (Те саме стосується інших відповідей, де використовується диктант )
Ентоні Конг,

Проста відповідь також буде: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 є корисним для спрощення доступу до даних, що зберігаються в класі (як у прикладі вище).

Якщо ви хочете лише одну динамічну властивість, перегляньте властивість () вбудовану функцію .


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'


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

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

Щоб відповісти на основне значення вашого запитання, ви хочете, щоб атрибут з читання, що не читається, був незмінним джерелом даних:

Мета - створити макет-клас, який веде себе як набір результатів db.

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


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

Я розглядав це, однак ви технічно переглядаєте список своїх помічників і геттерів. Через це кожен виклик буде повільнішим, оскільки спочатку ви шукаєте його зі списку, а не безпосередньо до нього. Якщо 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
це, здається, не працює. воно призначить результат властивості, а не саму власність.
mjallday

Це неправильно. Я приєдную динамічні властивості до своєї системи, не потребуючи ініціалізації класу. Ініціалізуйте як x = Example (), а потім додайте властивість до x.
Acecool

Якщо ви подивитесь на мій код, ви побачите, що я використовую клас ExampleBase: pass, потім клас Example (ExampleBase): ... тоді я додаю властивості до ExampleBase, тому що назва існує, і оскільки Example поширюється з нього, вона має доступ до всього. Я використовую __ var для помічника аксесуара, щоб мати можливість мати прямий доступ до об'єктів аксесуара, я використовую _ для збережених (необроблених) даних, які можуть бути "Ніхто", і немає підкреслень для фактичного властивості, яке проходить через геттер. Я можу викликати функцію getter за допомогою функції, що динамічно додається, або використовувати властивість. Все, не спершу починаючи.
Acecool

Примітка: я це згадував - але моє визначення для визначення означає, що посилання існує в просторі імен - тобто: class Example (Object): pass ... Він існує, але не був ініціалізований. Ініціалізація означає blah = Example (); тепер об'єкт "дублюється" та будується, а потім зберігається як посилання в бла. --- Якщо ви це зробите, то динамічно додані функції / властивості повинні бути лише в екземплярі - проблема, з якою у мене виникло це, навіть якщо функції існували, були випадки, коли я отримав помилку, кажучи, що їх немає. Або блокування помилки зупинило створення, або виконання асинхронізації.
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 / y та abs w / h тощо.).

Переосмислення внутрішніх геттерів / сеттерів - це нормальний шлях, але проблема полягає в тому, що вам потрібно зробити це для кожного класу або батьківського класу до цієї бази ... Це не працює для мене, як я вважаю за краще вільний вибір дітей / батьків на спадщину, дитячі вузли тощо.

Я створив рішення, яке відповідає на питання, не використовуючи тип даних Dict для подання даних, оскільки я вважаю, що це втомлює введення даних тощо.

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

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

Крім того, значення - це єдині нестатичні дані, які призначаються екземпляру класу, але фактичне властивість присвоюється класу, тобто речі, які ви не хочете повторювати, не потрібно повторювати ... можна призначити значення за замовчуванням, щоб геттер не потребував його щоразу, хоча є можливість змінити значення за замовчуванням за замовчуванням, і є інший варіант, щоб геттер повертав збережене значення, що не зберігається, замінюючи повернення за замовчуванням (зверніть увагу: цей метод означає, що необроблене значення присвоюється лише тоді, коли присвоєно значення, інакше воно не має значення - коли значення "Скидання", воно призначає "ні", тощо.)

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

Екземпляр / сировина, що зберігається, зберігається у класі., клас. посилається на клас Accessor, який містить статичні параметри / значення / функції для властивості. _клас. - це саме властивість, яке викликається при зверненні через клас екземпляра під час встановлення / отримання тощо.

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

Як було сказано, я також створюю безліч функцій, деякі з яких використовують інформацію про функції аксесуара, тому її не потрібно називати (оскільки це наразі трохи незручно - зараз вам потрібно використовувати _class. _class .UnctionName (_class_instance , аргументи) - Я обійшов за допомогою стека / трасування, щоб захопити посилання на екземпляр, щоб захопити значення, додавши функції, які виконують цей бітовий марафон, або додавши до нього об’єкт і використовуючи self (назвав це, щоб вказати на те, що вони - це екземпляр і зберегти доступ до себе, посилання класу AccessorFunc та інша інформація з визначень функції).

Це не зовсім зроблено, але це фантастичне утримання ніг. Примітка: Якщо ви не використовуєте __Name = AccessorFunc (...) для створення властивостей, ви не матимете доступу до клавіші __, хоча я визначаю її у функції init. Якщо ви це зробите, то проблем немає.

Також: Зауважте, що ім'я та ключ різні ... Ім'я є "формальним", яке використовується у створенні функції імен, а ключ призначений для зберігання та доступу до даних. тобто _class.x, де нижній регістр x є ключовим, ім'я буде великим регістром X, так що GetX () є функцією замість Getx (), яка виглядає трохи дивним. це дозволяє self.x працювати і виглядати доречно, але також дозволяє GetX () і виглядати відповідним.

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

Поточний список функцій, що використовують клавішу: x, ім'я: 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

Деякі дані, що виводяться, це:

Це для абсолютно нового класу, створеного за допомогою класу Demo без будь-яких даних, призначених, крім імені (щоб його можна було вивести), який є _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): ... - якщо ви знаєте рішення - опублікуйте його.

Єдине, що необхідно в класі - це рядки __ - 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 / тип даних / примусового застосування і т.д.

Наразі посилання знаходиться на (Цей посилання має відображати зміни до документа.): 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 в Package Manager, потім встановіть плагін - коли версія 1.0.0 буде готова, я додам його до основного списку плагінів ...)

Я сподіваюся, що це рішення допоможе ... і, як завжди:

Тільки тому, що це працює, це не робить правильно - Джош 'Acecool' Moser


Я хотів додати швидке відображення того, як виглядав клас, тому вам не потрібно відкривати файл коду, але коментарі не з'являються для його підтримки ..
Acecool

Мабуть, це набуває багато ненависті, що бентежить. Це робить саме те, про що вимагає ОП - динамічно додає властивості до об'єкта. Він також додає допоміжні функції, які не обов’язково включати - можливо, тому це стає ненавистю - а також гарантує, що розробник має простий спосіб отримати доступ до властивості (.x), яка обробляється через getter, сировину, що зберігається (._x), яка може бути Немає, коли .x повертає за замовчуванням або щось інше, і спосіб отримати доступ до аксесуара, щоб використовувати помічники, змінити речі тощо. (.__ x) ....
Acecool
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.