Jak dynamicznie dodawać właściwości do klasy?


215

Celem jest utworzenie próbnej klasy, która zachowuje się jak zestaw wyników db.

Na przykład, jeśli zapytanie bazy danych zwraca, używając wyrażenia dict {'ab':100, 'cd':200}, to chciałbym zobaczyć:

>>> dummy.ab
100

Na początku myślałem, że może mógłbym to zrobić w ten sposób:

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

ale c.abzamiast tego zwraca obiekt właściwości.

Zamiana setattrlinii k = property(lambda x: vs[i])na nic się nie przyda.

Więc jaki jest właściwy sposób na utworzenie właściwości instancji w czasie wykonywania?

PS Znam alternatywę przedstawioną w Jak stosowana jest __getattribute__metoda?


2
W kodzie znajduje się kilka literówek: definicja fn_readonly wymaga referencji :i . __init__self.fn_readyonly
mhawke

Masz rację. Dodałem tę funkcję ustawiającą w ostatniej chwili, aby podkreślić powód utworzenia właściwości w środowisku wykonawczym.
Anthony Kong

Główny problem, jaki miałem z tworzeniem właściwości przy inicjalizacji, polegał na tym, że w niektórych przypadkach, gdy szybko zadzwoniłem do pomocników lub wystąpił problem, dostałbym błąd, że nie istnieli pomimo tego, że tak robią. W poniższym rozwiązaniu tworzę 2 klasy. Jeden jako Base / Parent (którego próbuję znaleźć rozwiązanie, którego należy unikać) i główny obiekt, który rozszerza Base / Parent. Następnie w głównym obiekcie bez inicjowania wywołuję mojego twórcę AccessorFunc, który tworzy właściwości, funkcje pomocnicze i wiele innych.
Acecool

tj .: klasa ExampleBase: pass; klasa Przykład (ExampleBase): __x = Accessor (ExampleBase, 'x', 'X', 123); --- które tworzyłyby właściwość pod x i nazwanymi funkcjami przy użyciu X, więc GetX, SetX i więcej ... oraz .x, ._x i .__ x dla tej właściwości. .X jest więc samą właściwością, przez którą dane mają być przekazywane (pobieranie / ustawianie przez self.x = 123; lub self.x do wyjścia). Użyłem self._x dla przechowywanych danych RAW, aby były łatwo dostępne, ponieważ pozwoliłem także na przypisanie wartości domyślnych, bez ustawiania ich w przechowywanych danych. więc _x może być None, a .x może zwrócić 123. i .__ x powiązane z Accessorem
Acecool

Oto link do podstawowej wersji, która tworzy właściwości dynamiczne i funkcje dynamiczne - plik zawiera kilka linków do innych wersji. Jednym z nich jest system AccessorFunc wykorzystujący funkcję do tworzenia pomocników (jeden dla funkcji, jeden dla właściwości, jeden dla obu jako pojedynczych elementów - więc nie używa skracania kodu w niczym w tym pliku). Jeśli czegoś brakuje, jeden z inne pliki to: dropbox.com/s/phnnuavssmzeqrr/dynamic_properties_simple.py?dl=0
Acecool

Odpowiedzi:


333

Myślę, że powinienem rozszerzyć tę odpowiedź, skoro jestem starszy i mądrzejszy i wiem, co się dzieje. Lepiej późno niż wcale.

Państwo może dodać obiekt do dynamicznie klasy. Ale to jest haczyk: musisz dodać to do klasy .

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

A propertyjest właściwie prostą implementacją rzeczy zwanej deskryptorem . Jest to obiekt, który zapewnia niestandardową obsługę danego atrybutu w danej klasie . Trochę jak sposób na uwzględnienie ogromnego ifdrzewa __getattribute__.

Kiedy pytam o foo.bw powyższym przykładzie, Python widzi, że bzdefiniowana w klasie narzędzi do protokołu deskryptora -co po prostu oznacza, że jest to obiekt o __get__, __set__lub __delete__metody. Deskryptor twierdzi, że jest odpowiedzialny za obsługę tego atrybutu, dlatego Python wywołuje Foo.b.__get__(foo, Foo), a zwracana wartość jest przekazywana z powrotem jako wartość atrybutu. W przypadku propertykażdej z tych metod po prostu wywołuje metodęfget , fsetczy fdelzdałeś do propertykonstruktora.

Deskryptory są tak naprawdę sposobem Pythona na ujawnienie hydrauliki całej jego implementacji OO. W rzeczywistości istnieje inny typ deskryptora, który jest jeszcze bardziej powszechny niż 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>

Ta skromna metoda jest tylko innym rodzajem deskryptora. Jego __get__haczyki na instancji wywołującej jako pierwszy argument; w efekcie robi to:

def __get__(self, instance, owner):
    return functools.partial(self.function, instance)

Podejrzewam, że właśnie dlatego deskryptory działają tylko na klasach: są one formalizacją rzeczy, które napędzają klasy w pierwszej kolejności. Są nawet wyjątkiem od reguły: możesz oczywiście przypisać deskryptory do klasy, a same klasy są instancjami type! W rzeczywistości, próba odczytania Foo.bpołączeń nadal property.__get__; po prostu idiomatyczne jest, aby deskryptory zwracały się, gdy były dostępne jako atrybuty klasy.

Myślę, że to całkiem fajne, że praktycznie cały system OO Pythona może być wyrażony w Pythonie. :)

Aha, i napisałem niegrzeczny post na blogu o deskryptorach , jeśli jesteś zainteresowany.


35
Nie trzeba dodawać metody add_property. setattr (Foo, 'name', property (func))
Courtney D

8
Twoje „Ale to haczyk…” właśnie zaoszczędziło mi kilka godzin pracy. Dziękuję Ci.
Matt Howell,

2
Jeśli chcesz zdefiniować właściwość dla pojedynczej instancji, możesz utworzyć klasę w czasie wykonywania i zmodyfikować __class__ .
Wilfred Hughes

1
co z @ myproperty.setter? Jak dodać to dynamicznie?
LRMAAX

Nie musisz dodawać właściwości do zainicjowanego obiektu. Może to oznaczać, że przyklei się tylko do instancji, ale musiałbym to dwukrotnie sprawdzić. Wiem, że natrafiłem na podobny problem, w którym moje właściwości dynamiczne były tylko instancjami, skończyło się również na ustawieniu statycznym i tym, którego chciałem, który dotyczy obiektu, aby przyszłe inicjalizacje mogły z nich korzystać. Mój post jest poniżej i zawiera funkcje pomocnicze oraz łatwe sposoby na łatwy dostęp do wszystkiego. .x dla właściwości, ._x dla nieprzetworzonych danych, które getter / setter używa (które mogą być None), i .__ x dla obiektu akcesorium.
Acecool

57

Celem jest utworzenie próbnej klasy, która zachowuje się jak zestaw wyników db.

Więc czego chcesz, to słownik, w którym możesz przeliterować [„b”] jako ab?

To łatwe:

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

1
w bardziej ogólnej konfiguracji służy to ograniczonemu celowi. jeśli dyktat ma wielopoziomową hierarchię, np. d = {'a1': {'b': 'c'}, 'a2': ...}, to podczas gdy możesz zrobić d.a1 lub d.a2, możesz ' t do d.a1.b
Shreyas

1
Jedną z rzeczy, o których warto pamiętać jest to, że umożliwia to ustawienie wartości atrybutów dla atrybutów o tej samej nazwie, jak metody DICT lub atrybutów, ale nie pozwala na pobieranie wartości w taki sam sposób ponownie: d.items = 1, d.itemspowraca <built-in method items of atdict object at ...>. Możesz nadal robić d["items"]lub używać __getattribute__zamiast __getattr__, ale to uniemożliwia korzystanie z większości metod dykt.
Marcono1234,

Po prostu skorzystaj z biblioteki Muncha ! (rozwidlenie kiści)
Brian Peterson

38

Wydaje się, że możesz rozwiązać ten problem znacznie prościej namedtuple, ponieważ znasz całą listę pól z wyprzedzeniem.

from collections import namedtuple

Foo = namedtuple('Foo', ['bar', 'quux'])

foo = Foo(bar=13, quux=74)
print foo.bar, foo.quux

foo2 = Foo()  # error

Jeśli absolutnie musisz napisać własnego setera, musisz wykonać metaprogramowanie na poziomie klasy; property()nie działa na instancjach.


Świetny pomysł. Niestety w tej chwili utknąłem w Pythonie 2.4.
Anthony Kong


2
Facet, który napisał, namedtuplezasługuje na nagrodę za sprawienie, by było ono płynne i eleganckie, aby być wiernymi obiektowymi zasadami.
Keith Pinson

4
Niestety, w najlepszym razie ta odpowiedź dotyczy tylko specjalnego przypadku, w którym jedna klasa chciała składać się wyłącznie z atrybutów tylko do odczytu, które są znane z góry. Innymi słowy, nie sądzę, aby odnosiło się to do szerszego pytania o to, jak dodać ogólne właściwości - nie tylko tylko do odczytu - do klasy w czasie wykonywania (ani też obecna wersja innego „dodatku” nie odpowiada również opublikowane przez autora).
martineau,

@martineau, więc ... przekazać więcej argumentów property()? w żadnej odpowiedzi nie ma niczego, co byłoby specyficzne dla właściwości tylko do odczytu.
Eevee,

32

Nie musisz do tego używać właściwości. Po prostu zastąp, __setattr__aby uczynić je tylko do odczytu.

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

Tada

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

Jak dynamicznie dodawać właściwość do klasy python?

Załóżmy, że masz obiekt, do którego chcesz dodać właściwość. Zazwyczaj chcę używać właściwości, gdy muszę rozpocząć zarządzanie dostępem do atrybutu w kodzie, który ma dalsze wykorzystanie, aby móc zachować spójny interfejs API. Teraz zazwyczaj dodam je do kodu źródłowego, w którym obiekt jest zdefiniowany, ale załóżmy, że nie masz takiego dostępu lub musisz naprawdę dynamicznie wybierać funkcje programowo.

Utwórz klasę

Korzystając z przykładu opartego na dokumentacjiproperty , utwórzmy klasę obiektu z atrybutem „ukryty” i stwórzmy jego instancję:

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

o = C()

W Pythonie oczekujemy jednego oczywistego sposobu na robienie rzeczy. Jednak w tym przypadku pokażę dwa sposoby: z notacją dekoratora i bez. Po pierwsze, bez zapisu dekoratora. Może to być bardziej przydatne w przypadku dynamicznego przypisywania modułów pobierających, ustawiających lub usuwających.

Dynamiczny (aka Monkey Patching)

Stwórzmy trochę dla naszej klasy:

def getx(self):
    return self._x

def setx(self, value):
    self._x = value

def delx(self):
    del self._x

A teraz przypisujemy je do nieruchomości. Zauważ, że tutaj moglibyśmy wybrać nasze funkcje programowo, odpowiadając na dynamiczne pytanie:

C.x = property(getx, setx, delx, "I'm the 'x' property.")

I użycie:

>>> o.x = 'foo'
>>> o.x
'foo'
>>> del o.x
>>> print(o.x)
None
>>> help(C.x)
Help on property:

    I'm the 'x' property.

Dekoratorzy

Moglibyśmy zrobić to samo jak zrobiliśmy powyżej notacji dekorator, ale w tym przypadku musi nazwą Metody wszystkie taką samą nazwę (i polecam utrzymanie go tak samo jak atrybut), więc programowy zadanie nie jest tak trywialne jak używa powyższej metody:

@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

I przypisz obiekt właściwości wraz z ustawionymi ustawieniami i usuwaniem do klasy:

C.x = x

I użycie:

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

Zadałem podobne pytanie w tym wpisie Przepełnienie stosu, aby utworzyć fabrykę klas, która stworzyła proste typy. Rezultatem była ta odpowiedź, która miała działającą wersję fabryki klasy. Oto fragment odpowiedzi:

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

Możesz użyć pewnej odmiany tego, aby stworzyć wartości domyślne, które są twoim celem (istnieje również odpowiedź na to pytanie, które dotyczy tego).


4

Nie jestem pewien, czy całkowicie rozumiem pytanie, ale można zmodyfikować właściwości instancji w czasie wykonywania za pomocą wbudowanej __dict__klasy:

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

Zasadniczo moim pytaniem jest dowiedzieć się, czy można utworzyć nową właściwość w środowisku wykonawczym. Konsensus wydaje się być negatywny. Twoja sugestia jest z pewnością prosta i praktyczna. (To samo co w przypadku innych odpowiedzi, które używają słownika )
Anthony Kong

Prosta odpowiedź brzmiałaby również:self.__dict__[key] = value
Allan Karlson

4

Dla tych, którzy pochodzą z wyszukiwarek, oto dwie rzeczy, których szukałem, mówiąc o właściwościach dynamicznych :

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__ jest dobre, jeśli chcesz umieścić dynamicznie tworzone właściwości. __getattr__dobrze jest zrobić coś tylko wtedy, gdy potrzebna jest wartość, na przykład wykonać zapytanie do bazy danych. Kombinacja set / get jest dobra, aby uprościć dostęp do danych przechowywanych w klasie (jak w powyższym przykładzie).

Jeśli chcesz tylko jednej właściwości dynamicznej, spójrz na wbudowaną funkcję property () .


4

Nie można dodać nowego property()do instancji w czasie wykonywania, ponieważ właściwości są deskryptorami danych. Zamiast tego musisz dynamicznie tworzyć nową klasę lub przeciążać __getattribute__, aby przetwarzać deskryptory danych w instancjach.


To jest źle. Możesz dodać właściwość do klasy, a następnie uzyskać do niej dostęp z metody
Ahmed

2

Najlepszym sposobem na osiągnięcie tego jest zdefiniowanie __slots__. W ten sposób twoje instancje nie mogą mieć nowych atrybutów.

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

To drukuje 12

    c.ab = 33

To daje: AttributeError: 'C' object has no attribute 'ab'


2

Kolejny przykład, jak osiągnąć pożądany efekt

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)

Teraz możemy robić rzeczy takie jak:

>>> foo = Foo('baz')
>>> foo.baz = 5
>>> foo.bar
5
>>> foo.baz
5

2

Oto rozwiązanie, które:

  • Umożliwia określenie nazw właściwości jako ciągów , dzięki czemu mogą pochodzić z zewnętrznego źródła danych, a nie z wszystkich wymienionych w programie.
  • Dodaje właściwości, gdy klasa jest zdefiniowana , zamiast za każdym razem, gdy tworzony jest obiekt.

Po zdefiniowaniu klasy po prostu zrób to, aby dynamicznie dodać do niej właściwość:

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

Oto kompletny przykład przetestowany w Pythonie 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

Możesz użyć następującego kodu, aby zaktualizować atrybuty klasy za pomocą obiektu słownika:

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

To jest trochę inne niż to, czego chciał OP, ale zatrzeszczałem mózg, dopóki nie znalazłem działającego rozwiązania, więc stawiam tutaj dla następnego faceta / dziewczyny

Potrzebowałem sposobu na określenie dynamicznych ustawiaczy i pobierających.

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)

Znam swoje pola z wyprzedzeniem, więc zamierzam stworzyć swoje właściwości. UWAGA: nie możesz tego zrobić NA instancji, te właściwości będą istnieć w klasie !!!

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

Przetestujmy to teraz…

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

Czy to jest mylące? Tak, przepraszam, że nie mogłem wymyślić żadnych znaczących przykładów z prawdziwego świata. Nie dotyczy to także lekkich serc.


Jeśli dobrze pamiętam, podczas wszystkich moich testów znalazłem sposób na utworzenie właściwości typu STATIC / dynamicznie dodawanego g / settera. Musiałbym przejść przez wszystkie poprzednie - ale możliwość dodania czegoś, co jest wspólne dla wszystkich instancji, jest zdecydowanie możliwa. Jeśli chodzi o tworzenie poszczególnych procesów ... Jestem pewien, że możesz, aby jedno wystąpienie miało coś innego. Musiałbym to zweryfikować, ale też wpadłem na coś takiego (podczas pierwszych prób popełniłem błąd, który spowodował utworzenie funkcji, ale nie wszystkie instancje miały je z powodu wady)
Acecool,

Ponadto każde możliwe rozwiązanie jest mile widziane, ponieważ jest to repozytorium wiedzy. To również ekscytujące widzieć różne sposoby, w jakie różni ludzie tworzą rozwiązania problemu. Moje rozwiązanie ma DUŻO, zmniejszyłeś to do czegoś prostszego do udostępnienia. Zrobiłem też mój mniejszy wariant - powinien być gdzieś w tym temacie - i właśnie zdałem sobie sprawę, że to nie ten, który zamieściłem: -) ...
Acecool

0

To wydaje się działać (ale patrz poniżej):

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

Jeśli potrzebujesz bardziej złożonego zachowania, zredaguj swoją odpowiedź.

edytować

W przypadku dużych zestawów danych prawdopodobnie bardziej wydajna byłaby pamięć:

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

Aby odpowiedzieć na główny wątek pytania, potrzebujesz atrybutu tylko do odczytu ze słownika jako niezmiennego źródła danych:

Celem jest utworzenie próbnej klasy, która zachowuje się jak zestaw wyników db.

Na przykład, jeśli zapytanie bazy danych zwraca, używając wyrażenia dict {'ab':100, 'cd':200}, to chciałbym zobaczyć

>>> dummy.ab
100

Pokażę, jak korzystać z namedtuple z collectionsmodułu, aby osiągnąć to:

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

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

Wyjście to:

>> 1

0

Rozszerzanie pomysłu z 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)

Wynik:

<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

Chociaż podano wiele odpowiedzi, nie mogłem znaleźć takiej, z której jestem zadowolony. Wymyśliłem własne rozwiązanie, które propertydziała w przypadku dynamicznego przypadku. Źródło odpowiedzi na pierwotne pytanie:

#!/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

Coś, co działa dla mnie, to:

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)

Wynik

a
aa

-1

Niedawno napotkałem podobny problem, rozwiązanie, które wymyśliłem, __getattr__a __setattr__dla właściwości, które chcę obsłużyć, wszystko inne jest przekazywane do oryginałów.

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

Przyjrzałem się temu, jednak technicznie przeglądasz listę w programach pobierających i ustawiających. Z tego powodu każde połączenie będzie działało wolniej, ponieważ najpierw przeglądasz je z listy, a nie z bezpośredniego dostępu. Chyba że Python automatycznie zmapuje go dla ciebie; może, ale nie sprawdziłem tego jeszcze na pewno, ale jestem pewien, że zanim spróbuję. Po drugie, robiąc to, musisz zdefiniować pomocników w inny sposób. Nie można także blokować typów danych i / lub wartości bez dużego słownika lub wielu dodatkowych wierszy.
Acecool

tj .: albo musiałbym stworzyć klasę podstawową, z której rozszerzę wszystkie moje dzieci korzystające z systemu, LUB LUB muszę dodać do wszystkiego funkcje magiczne s / getattr i za każdym razem duplikować system. Deklaracja właściwości oznacza również, że musisz skonfigurować je w jeden sposób, a jeśli chcesz skorzystać z dodatkowego wsparcia, takiego jak wymieniłem, takiego jak ochrona danych i / lub wartości, aby zezwolić lub zapobiec przypisaniu danych i innym pomocnikom , to musisz je zakodować. To prawda, że ​​możesz sprawić, by system był podobny w zachowaniu, ale ostatecznie skończy się to tym, że zadeklarujesz nieco inaczej i będzie on większy.
Acecool

-1

Oto prosty przykład programowego tworzenia obiektu właściwości.

#!/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

Jedynym sposobem na dynamiczne dołączenie właściwości jest utworzenie nowej klasy i jej wystąpienia za pomocą nowej właściwości.

class Holder: p = property(lambda x: vs[i], self.fn_readonly)
setattr(self, k, Holder().p)

1
to nie wydaje się działać. przypisze wynik właściwości, a nie samą właściwość.
mjallday

To jest niepoprawne. Dołączam właściwości dynamiczne do mojego systemu bez konieczności inicjowania klasy. Zainicjuj to x = Example (), a następnie dodaj właściwość do x.
Acecool

Jeśli spojrzysz na mój kod, zobaczysz, że używam klasy ExampleBase: pass, następnie class Example (ExampleBase): ... następnie dołączam właściwości do ExampleBase, ponieważ nazwa wtedy istnieje, a ponieważ przykład się z niej wywodzi, ma dostęp do wszystkiego. Używam __ var, aby pomocnik modułu dostępu mógł mieć bezpośredni dostęp do obiektów modułu dostępu, używam _ dla danych przechowywanych (surowych), które mogą być Brak, i żadnych znaków podkreślenia dla faktycznej właściwości przechodzącej przez moduł pobierający. Mogę wywołać funkcję gettera za pomocą funkcji dodawanej dynamicznie lub użyć właściwości. Wszystko bez pierwszej inicjacji.
Acecool

Uwaga: wspomniałem o tym - ale moja definicja definicji oznacza, że ​​odwołanie istnieje w przestrzeni nazw - tj .: class Przykład (Object): pass ... Istnieje, ale nie został zainicjowany. Inicjalizacja oznacza bla = Example (); teraz obiekt został „zduplikowany” i skonstruowany, a następnie zapisany jako odniesienie w bla. --- Jeśli to zrobisz, wówczas funkcje / właściwości dodane dynamicznie powinny znajdować się tylko w instancji - problem z tym miałem, nawet jeśli funkcje istniały, były przypadki, w których dostałem błąd, mówiąc, że nie. Błąd blokowania został zatrzymany podczas tworzenia lub wykonanie asynchroniczne.
Acecool

Kilka innych uwag: Możliwe jest dynamiczne tworzenie właściwości i działa w sposób, który istnieje tylko dla jednej instancji. Możesz także zrobić je tak, aby istniały dla obiektu (czego w większości przypadków potrzebujesz). Są też przypadki, w których dodane elementy są „statyczne”, tj.
Te

-6

Wiele z dostarczonych odpowiedzi wymaga tak wielu wierszy na właściwość, tj. / I / lub - co uważam za brzydką lub żmudną implementację ze względu na powtarzalność wymaganą dla wielu właściwości itp. Wolę utrzymywać gotowanie na niskim poziomie / uprościć je, dopóki nie nie można go już uprościć, dopóki nie przyniesie to zbytniego celu.

W skrócie: w ukończonych pracach, jeśli powtórzę 2 wiersze kodu, zazwyczaj przekształcam go w funkcję pomocniczą z jednym wierszem itd. ... Upraszczam matematykę lub nieparzyste argumenty, takie jak (start_x, start_y, end_x, end_y) do (x, y, w, h) tj. x, y, x + w, y + h (czasami wymagające min / max lub jeśli w / h są ujemne i implementacja tego nie lubi, odejmę od x / y i abs w / h. itp.).

Zastąpienie wewnętrznych modułów pobierających / ustawiających jest dobrym rozwiązaniem, ale problem polega na tym, że musisz to zrobić dla każdej klasy lub nadać klasę tej bazie ... To nie działa dla mnie tak, jak wolałbym być swobodnie wybierać dzieci / rodziców do dziedziczenia, węzłów dziecięcych itp.

Stworzyłem rozwiązanie, które odpowiada na pytanie bez użycia typu danych Dict do dostarczania danych, ponieważ uważam, że wprowadzanie danych jest uciążliwe itp.

Moje rozwiązanie wymaga dodania 2 dodatkowych linii nad klasą, aby utworzyć klasę podstawową dla klasy, do której chcesz dodać właściwości, a następnie 1 linii na i masz możliwość dodania wywołań zwrotnych w celu kontrolowania danych, informowania o zmianach danych , ogranicz dane, które można ustawić na podstawie wartości i / lub typu danych, i wiele więcej.

Masz również opcję użycia _object.x, _object.x = wartość, _object.GetX (), _object.SetX (wartość) i są one obsługiwane w równoważny sposób.

Dodatkowo wartości są jedynymi danymi niestatycznymi, które są przypisane do instancji klasy, ale faktyczna właściwość jest przypisana do klasy, co oznacza rzeczy, których nie chcesz powtarzać, nie trzeba powtarzać ... może przypisać wartość domyślną, aby moduł pobierający nie potrzebował jej za każdym razem, chociaż istnieje opcja zastąpienia domyślnej wartości domyślnej, a istnieje inna opcja, więc moduł pobierający zwraca nieprzetworzoną przechowywaną wartość poprzez przesłanianie wartości domyślnych (uwaga: ta metoda oznacza, że ​​surowa wartość jest przypisywana tylko wtedy, gdy przypisana jest wartość, w przeciwnym razie jest to Brak - gdy wartość to Resetuj, wówczas przypisuje Brak, itp.)

Istnieje również wiele funkcji pomocniczych - pierwsza właściwość, która zostanie dodana, dodaje do klasy około 2 pomocników do odwoływania się do wartości instancji ... Są to ResetAccessors (_key, ..) powtarzane varargs (wszystkie mogą być powtarzane przy użyciu nazwanych args ) i SetAccessors (_key, _value) z opcją dodawania więcej do klasy głównej w celu zwiększenia wydajności - planowane są: sposób na grupowanie akcesoriów, więc jeśli masz tendencję do resetowania kilku na raz, za każdym razem , możesz przypisać je do grupy i zresetować grupę zamiast powtarzania nazwanych kluczy za każdym razem i więcej.

Instancja / nieprzetworzona wartość przechowywana jest przechowywana w klasie. , klasa. odwołuje się do klasy Accessor, która przechowuje statyczne zmienne / wartości / funkcje dla właściwości. _klasa. jest samą właściwością, która jest wywoływana przy dostępie przez klasę instancji podczas ustawiania / pobierania itp.

Accessor _class .__ wskazuje na klasę, ale ponieważ jest ona wewnętrzna, należy ją przypisać do klasy, dlatego zdecydowałem się użyć __Name = AccessorFunc (...) do przypisania jej, pojedynczej linii na właściwość z wieloma opcjonalnymi argumenty do użycia (przy użyciu kluczowanych varargs, ponieważ są łatwiejsze i bardziej wydajne do identyfikacji i utrzymania) ...

Tworzę również wiele funkcji, jak wspomniano, niektóre z nich używają informacji o funkcji akcesorium, więc nie trzeba ich wywoływać (ponieważ w tej chwili jest to trochę niewygodne - w tej chwili musisz użyć _class. .FunctionName (_class_instance , args) - Poruszałem się przy użyciu stosu / śledzenia, aby pobrać odwołanie do instancji, aby pobrać wartość, dodając funkcje, które albo uruchamiają ten bit maratonu, albo dodając akcesory do obiektu i używając self (nazwał to, aby zaznaczyć, że dotyczy instancji i aby zachować dostęp do siebie, odwołania do klasy AccessorFunc i innych informacji z definicji funkcji).

Nie jest to do końca zrobione, ale jest fantastyczną stopą. Uwaga: Jeśli nie użyjesz __Name = AccessorFunc (...) do utworzenia właściwości, nie będziesz miał dostępu do klucza __, nawet jeśli zdefiniuję go w funkcji init. Jeśli to zrobisz, nie będzie żadnych problemów.

Pamiętaj też, że nazwa i klucz są różne ... Nazwa jest „formalna”, używana w funkcji tworzenia nazw funkcji, a klucz służy do przechowywania danych i dostępu do nich. tj. _class.x, gdzie mała litera x jest kluczem, nazwa będzie wielką literą X, więc GetX () jest funkcją zamiast Getx (), która wygląda trochę dziwnie. pozwala to self.x działać i wyglądać odpowiednio, ale także pozwalać GetX () i wyglądać odpowiednio.

Mam przykładową klasę skonfigurowaną z identycznym kluczem / nazwą i inną do pokazania. wiele funkcji pomocniczych utworzonych w celu wyprowadzenia danych (uwaga: nie wszystko jest kompletne), dzięki czemu można zobaczyć, co się dzieje.

Aktualna lista funkcji wykorzystujących klawisz: x, nazwa: X wyświetla jako:

Nie jest to bynajmniej wyczerpująca lista - jest kilka, które nie dotarły do ​​tego w momencie publikowania ...

_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

Niektóre dane wyjściowe to:

To jest dla zupełnie nowej klasy utworzonej przy użyciu klasy Demo bez przypisanych danych innych niż nazwa (aby można ją było wydrukować), czyli _foo, nazwa zmiennej, której użyłem ...

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

I to po przypisaniu wszystkich właściwości _foo (oprócz nazwy) następujących wartości w tej samej kolejności: „ciąg”, 1,0, prawda, 9, 10, fałsz

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

Pamiętaj, że z powodu ograniczonych typów danych lub ograniczeń wartości niektóre dane nie zostały przypisane - jest to zgodne z projektem. Seter zabrania przypisywania złych typów danych lub wartości, nawet przypisywania ich jako wartości domyślnej (chyba że zastąpisz domyślne zachowanie ochrony wartości)

Kod nie został tu opublikowany, ponieważ nie miałem miejsca po przykładach i objaśnieniach ... Również dlatego, że się zmieni.

Uwaga: w momencie tego postu plik jest nieuporządkowany - to się zmieni. Ale jeśli uruchomisz go w Sublime Text i skompilujesz, lub uruchomisz go z Pythona, skompiluje i wypluje mnóstwo informacji - część AccessorDB nie jest gotowa (która zostanie użyta do aktualizacji Print Getters i GetKeyOutput pomocnika funkcje wraz ze zmianą na funkcję Instancji, prawdopodobnie umieszczone w jednej funkcji i zmienione jej nazwy - poszukaj jej ...)

Dalej: Nie wszystko jest wymagane, aby działało - wiele skomentowanych rzeczy na dole zawiera więcej informacji wykorzystywanych do debugowania - może nie być tam podczas pobierania. Jeśli tak, powinieneś być w stanie odkomentować i skompilować ponownie, aby uzyskać więcej informacji.

Szukam obejścia potrzebującego MyClassBase: pass, MyClass (MyClassBase): ... - jeśli znasz rozwiązanie - opublikuj je.

Jedyne, co jest potrzebne w klasie, to wiersze __ - str służy do debugowania, podobnie jak init - można je usunąć z klasy demonstracyjnej, ale trzeba będzie skomentować lub usunąć niektóre z poniższych wierszy (_foo / 2/3 ) ..

Klasy String, Dict i Util u góry są częścią mojej biblioteki Python - nie są kompletne. Skopiowałem kilka rzeczy z biblioteki i stworzyłem kilka nowych. Pełny kod będzie łączył się z pełną biblioteką i będzie ją zawierał wraz z dostarczaniem zaktualizowanych wywołań i usuwaniem kodu (w rzeczywistości jedynym kodem, który pozostanie, będzie klasa demonstracyjna i instrukcje drukowania - system AccessorFunc zostanie przeniesiony do biblioteki). ..

Część pliku:

##
## 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 = { } )

To piękno sprawia, że ​​niezwykle łatwo jest tworzyć nowe klasy z dynamicznie dodawanymi właściwościami za pomocą AccessorFuncs / callback / data-type / value enforcement itp.

Na razie link znajduje się pod adresem (ten link powinien odzwierciedlać zmiany w dokumencie.): Https://www.dropbox.com/s/6gzi44i7dh58v61/dynamic_properties_accessorfuncs_and_more.py?dl=0

Ponadto: jeśli nie używasz Sublime Text, polecam go ponad Notepad ++, Atom, Visual Code i inne ze względu na prawidłowe implementacje wątków, dzięki czemu korzystanie z niego jest znacznie, znacznie szybsze ... Pracuję również nad kodem podobnym do IDE system mapowania dla niego - spójrz na: https://bitbucket.org/Acecool/acecoolcodemappingsystem/src/master/ (najpierw dodaj repozytorium w Menedżerze pakietów, a następnie zainstaluj wtyczkę - gdy wersja 1.0.0 będzie gotowa, dodam do głównej listy wtyczek ...)

Mam nadzieję, że to rozwiązanie pomoże ... i jak zawsze:

Tylko dlatego, że działa, nie robi tego dobrze - Josh „Acecool” Moser


Chciałem dodać szybki przegląd tego, jak wyglądała klasa, więc nie trzeba otwierać pliku kodu, ale komentarze nie wydają się go wspierać.
Acecool

Najwyraźniej robi się to z nienawiści, co jest mylące. Robi dokładnie to, o co prosi OP - dynamicznie dodaje właściwości do obiektu. Dodaje także funkcje pomocnicze, których nie trzeba włączać - być może dlatego zaczyna się nienawidzić - i zapewnia również deweloperowi łatwy dostęp do właściwości (.x), która jest przetwarzana przez moduł pobierający, surowa wartość przechowywana (._x), która może być Brak, gdy .x zwraca wartość domyślną lub coś innego, a także sposób na dostęp do akcesorium w celu korzystania z pomocników, zmiany rzeczy itp. (.__ x) ....
Acecool
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.