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.ab
zamiast tego zwraca obiekt właściwości.
Zamiana setattr
linii 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?
python
properties
runtime
monkeypatching
Anthony Kong
źródło
źródło
:
i .__init__
self.fn_readyonly
Odpowiedzi:
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 .
A
property
jest 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 ogromnegoif
drzewa__getattribute__
.Kiedy pytam o
foo.b
w powyższym przykładzie, Python widzi, żeb
zdefiniowana 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łujeFoo.b.__get__(foo, Foo)
, a zwracana wartość jest przekazywana z powrotem jako wartość atrybutu. W przypadkuproperty
każdej z tych metod po prostu wywołuje metodęfget
,fset
czyfdel
zdałeś doproperty
konstruktora.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
.Ta skromna metoda jest tylko innym rodzajem deskryptora. Jego
__get__
haczyki na instancji wywołującej jako pierwszy argument; w efekcie robi to: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 odczytaniaFoo.b
połączeń nadalproperty.__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.
źródło
setattr (Foo, 'name', property (func))
Więc czego chcesz, to słownik, w którym możesz przeliterować [„b”] jako ab?
To łatwe:
źródło
d.items = 1
,d.items
powraca<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.Wydaje się, że możesz rozwiązać ten problem znacznie prościej
namedtuple
, ponieważ znasz całą listę pól z wyprzedzeniem.Jeśli absolutnie musisz napisać własnego setera, musisz wykonać metaprogramowanie na poziomie klasy;
property()
nie działa na instancjach.źródło
namedtuple
zasługuje na nagrodę za sprawienie, by było ono płynne i eleganckie, aby być wiernymi obiektowymi zasadami.property()
? w żadnej odpowiedzi nie ma niczego, co byłoby specyficzne dla właściwości tylko do odczytu.Nie musisz do tego używać właściwości. Po prostu zastąp,
__setattr__
aby uczynić je tylko do odczytu.Tada
źródło
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 dokumentacji
property
, utwórzmy klasę obiektu z atrybutem „ukryty” i stwórzmy jego instancję: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:
A teraz przypisujemy je do nieruchomości. Zauważ, że tutaj moglibyśmy wybrać nasze funkcje programowo, odpowiadając na dynamiczne pytanie:
I użycie:
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:
I przypisz obiekt właściwości wraz z ustawionymi ustawieniami i usuwaniem do klasy:
I użycie:
źródło
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:
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).
źródło
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:źródło
self.__dict__[key] = value
Dla tych, którzy pochodzą z wyszukiwarek, oto dwie rzeczy, których szukałem, mówiąc o właściwościach dynamicznych :
__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 () .
źródło
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.źródło
Najlepszym sposobem na osiągnięcie tego jest zdefiniowanie
__slots__
. W ten sposób twoje instancje nie mogą mieć nowych atrybutów.To drukuje
12
To daje:
AttributeError: 'C' object has no attribute 'ab'
źródło
Kolejny przykład, jak osiągnąć pożądany efekt
Teraz możemy robić rzeczy takie jak:
źródło
Oto rozwiązanie, które:
Po zdefiniowaniu klasy po prostu zrób to, aby dynamicznie dodać do niej właściwość:
Oto kompletny przykład przetestowany w Pythonie 3:
źródło
Możesz użyć następującego kodu, aby zaktualizować atrybuty klasy za pomocą obiektu słownika:
źródło
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.
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 !!!
Przetestujmy to teraz…
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.
źródło
To wydaje się działać (ale patrz poniżej):
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ęć:
źródło
Aby odpowiedzieć na główny wątek pytania, potrzebujesz atrybutu tylko do odczytu ze słownika jako niezmiennego źródła danych:
Pokażę, jak korzystać z
namedtuple
zcollections
modułu, aby osiągnąć to:zwroty
100
źródło
Wyjście to:
źródło
Rozszerzanie pomysłu z kjfletch
Wynik:
źródło
Chociaż podano wiele odpowiedzi, nie mogłem znaleźć takiej, z której jestem zadowolony. Wymyśliłem własne rozwiązanie, które
property
działa w przypadku dynamicznego przypadku. Źródło odpowiedzi na pierwotne pytanie:źródło
Coś, co działa dla mnie, to:
Wynik
źródło
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.źródło
Oto prosty przykład programowego tworzenia obiektu właściwości.
źródło
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.
źródło
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 ...
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 ...
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
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:
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
źródło