Dzięki właściwościom Pythona mogę to zrobić
obj.y
wywołuje funkcję, a nie tylko zwraca wartość.
Czy można to zrobić za pomocą modułów? Mam przypadek, w którym chcę
module.y
aby wywołać funkcję, zamiast zwracać przechowywaną tam wartość.
python
properties
python-module
Josh Gibson
źródło
źródło
__getattr__
moduł dla bardziej nowoczesnego rozwiązania.Odpowiedzi:
Tylko wystąpienia klas w nowym stylu mogą mieć właściwości. Możesz przekonać Pythona, że taka instancja jest modułem, przechowując ją w
sys.modules[thename] = theinstance
. Na przykład twój plik modułu m.py może wyglądać następująco:źródło
types.ModuleType
jak pokazano w bardzo podobnej odpowiedzi @ Unknown?builtins.module
, które same w sobie są instancjamitype
(co jest definicją klasy w nowym stylu). Problem polega na tym, że właściwości muszą znajdować się w klasie, a nie w instancji: jeśli to zrobiszf = Foo()
,f.some_property = property(...)
zakończy się niepowodzeniem w taki sam sposób, jak gdybyś naiwnie umieścił ją w module. Rozwiązaniem jest umieszczenie go w klasie, ale ponieważ nie chcesz, aby wszystkie moduły miały tę właściwość, tworzysz podklasę (zobacz odpowiedź Nieznane).globals()
(utrzymywanie kluczy w stanie nienaruszonym, ale resetowanie wartościNone
) po ponownym powiązaniu nazwy wsys.modules
jest problemem Pythona 2 - Python 3.4 działa zgodnie z przeznaczeniem. Jeśli potrzebujesz dostępu do obiektu klasy w Py2, dodaj np. Bezpośrednio_M._cls = _M
zaclass
instrukcją (lub schowaj ją równoważnie w jakiejś innej przestrzeni nazw) i uzyskaj do niego dostęp tak jakself._cls
w metodach, które tego wymagają (type(self)
może być OK, ale nie, jeśli wykonasz również jakąkolwiek podklasę_M
) .Zrobiłbym to, aby poprawnie odziedziczyć wszystkie atrybuty modułu i zostać poprawnie zidentyfikowanym przez isinstance ()
Następnie możesz wstawić to do sys.modules:
źródło
__file__
które muszą być zdefiniowane ręcznie, (2) importy wykonane w module zawierającym klasę nie będą "widoczne" w czasie wykonywania itd ...types.ModuleType
, każda klasa (w nowym stylu) wystarczy. Jakie dokładnie atrybuty modułu mają zostać odziedziczone?__init__
wystąpi, i uzyskasz prawidłowe zachowanie podczas używaniaisinstance
.Ponieważ PEP 562 został zaimplementowany w Pythonie> = 3.7, teraz możemy to zrobić
plik: module.py
stosowanie:
Zauważ, że jeśli pominiesz
raise AttributeError
w__getattr__
funkcji, oznacza to, że funkcja kończy się nareturn None
, a następniemodule.nosuch
otrzyma wartośćNone
.źródło
Na podstawie odpowiedzi Johna Lina :
Użycie (zwróć uwagę na wiodące podkreślenie), w
the_module.py
:Następnie:
Główny znak podkreślenia jest niezbędny do odróżnienia funkcji przypisanej własności od funkcji oryginalnej. Nie mogłem wymyślić sposobu na ponowne przypisanie identyfikatora, ponieważ w czasie wykonywania dekoratora nie został on jeszcze przypisany.
Zauważ, że IDE nie będą wiedzieć, że właściwość istnieje i pokażą czerwone fale.
źródło
@property def x(self): return self._x
myślę, żedef thing()
bez podkreślenia jest bardziej konwencjonalne. Czy możesz również utworzyć dekorator „ustawiający właściwości modułu” w swojej odpowiedzi?def thing()
sugestię. Problem polega na tym, że__getattr__
jest wywoływany tylko w przypadku brakujących atrybutów . Ale po@module_property def thing(): …
uruchomieniachthe_module.thing
jest zdefiniowane, więc getattr nigdy nie zostanie wywołane. Musimy jakoś zarejestrować sięthing
w dekoratorze, a następnie usunąć go z przestrzeni nazw modułu. Próbowałem wrócićNone
od dekoratora, ale potemthing
jest zdefiniowany jakoNone
. Można to zrobić,@module_property def thing(): … del thing
ale uważam, że jest to gorsze niż używaniething()
jako funkcji__getattribute__
”. Dziękuję Ci.Typowym przypadkiem użycia jest: wzbogacenie (ogromnego) istniejącego modułu kilkoma (kilkoma) atrybutami dynamicznymi - bez przekształcania wszystkich elementów modułu w układ klasy. Niestety najprostsza łatka na klasę modułu, jak na przykład,
sys.modules[__name__].__class__ = MyPropertyModule
nie działaTypeError: __class__ assignment: only for heap types
. Dlatego tworzenie modułów wymaga ponownego okablowania.To podejście robi to bez haków importu Pythona, po prostu mając prolog na górze kodu modułu:
Stosowanie:
Uwaga: coś podobnego
from propertymodule import dynval
spowoduje oczywiście zamrożoną kopię - odpowiadającądynval = someobject.dynval
źródło
Krótka odpowiedź: użyj
proxy_tools
proxy_tools
Pakiet stara się zapewnić@module_property
funkcjonalność.Instaluje się z
Używając niewielkiej modyfikacji przykładu @ Marein,
the_module.py
umieściliśmyTeraz z innego skryptu mogę zrobić
Nieoczekiwane zachowanie
To rozwiązanie nie jest pozbawione zastrzeżeń. Mianowicie nie
the_module.thing
jest to ciąg ! Jest toproxy_tools.Proxy
obiekt, którego specjalne metody zostały nadpisane, tak że naśladuje łańcuch. Oto kilka podstawowych testów, które ilustrują ten punkt:Wewnętrznie oryginalna funkcja jest przechowywana w
the_module.thing._Proxy__local
:Dalsze przemyślenia
Szczerze mówiąc, zastanawiam się, dlaczego moduły nie mają wbudowanej tej funkcji. Myślę, że sedno sprawy polega na tym, że
the_module
jest to instancjatypes.ModuleType
klasy. Ustawienie „właściwości modułu” jest równoznaczne z ustawieniem właściwości instancji tej klasy, a nietypes.ModuleType
samej klasy. Aby uzyskać więcej informacji, zobacz tę odpowiedź .Możemy faktycznie zaimplementować właściwości w
types.ModuleType
następujący sposób, chociaż wyniki nie są świetne. Nie możemy bezpośrednio modyfikować typów wbudowanych, ale możemy je przeklinać :Daje nam to właściwość, która istnieje we wszystkich modułach. Jest to trochę nieporęczne, ponieważ przerywamy zachowanie ustawień we wszystkich modułach:
źródło
@module_property
dekoratora. Ogólnie rzecz biorąc, wbudowany@property
dekorator jest używany podczas definiowania klasy, a nie po utworzeniu jej instancji, więc zakładam, że to samo dotyczy właściwości modułu i tak jest z odpowiedzią Alexa - przypomnij sobie to pytanie „Czy moduły mogą mieć takie same właściwości, jak obiekty?”. Jednak możliwe jest ich późniejsze dodanie i zmodyfikowałem mój wcześniejszy fragment, aby zilustrować sposób, w jaki można to zrobić.cached_module_property
, fakt, że__getattr__()
nie będzie już wywoływany, jeśli atrybut zostanie zdefiniowany, jest pomocny. (podobnie do tego, cofunctools.cached_property
osiąga).