Występują tutaj dwa podstawowe problemy:
__xxx__
metody są wyszukiwane tylko w klasie
TypeError: can't set attributes of built-in/extension type 'module'
(1) oznacza, że każde rozwiązanie musiałoby również śledzić, który moduł był badany, w przeciwnym razie każdy moduł miałby wówczas zachowanie polegające na zastępowaniu instancji; a (2) oznacza, że (1) nie jest nawet możliwe ... przynajmniej nie bezpośrednio.
Na szczęście sys.modules nie jest wybredny, jeśli chodzi o to, co tam jest, więc opakowanie będzie działać, ale tylko w przypadku dostępu do modułu (tj. Aby uzyskać dostęp import somemodule; somemodule.salutation('world')
do tego samego modułu, musisz prawie globals()
wyciągnąć metody z klasy podstawienia i dodać je do eiher za pomocą niestandardową metodę w klasie (lubię używać .export()
) lub z funkcją ogólną (taką jak te już wymienione jako odpowiedzi). Należy pamiętać o jednej rzeczy: jeśli opakowanie tworzy za każdym razem nową instancję, a rozwiązanie globalne nie, kończysz z subtelnie innym zachowaniem. Och, i nie możesz używać obu jednocześnie - to jedno lub drugie.
Aktualizacja
Od Guido van Rossuma :
W rzeczywistości istnieje hack, który jest czasami używany i zalecany: moduł może zdefiniować klasę z pożądaną funkcjonalnością, a na koniec zamienić się w sys.modules na instancję tej klasy (lub na klasę, jeśli nalegasz , ale to generalnie mniej przydatne). Na przykład:
# module foo.py
import sys
class Foo:
def funct1(self, <args>): <code>
def funct2(self, <args>): <code>
sys.modules[__name__] = Foo()
Działa to, ponieważ maszyna importująca aktywnie włącza ten hack i jako ostatni krok wyciąga rzeczywisty moduł z sys.modules po załadowaniu go. (To nie przypadek. Hack został zaproponowany dawno temu i zdecydowaliśmy, że podoba nam się wystarczająco, aby wspierać go w imporcie maszyn).
Tak więc ustalonym sposobem osiągnięcia tego, co chcesz, jest utworzenie pojedynczej klasy w swoim module i jako ostatni akt modułu zastąp sys.modules[__name__]
go instancją swojej klasy - a teraz możesz grać z __getattr__
/ __setattr__
/ __getattribute__
w razie potrzeby.
Uwaga 1 : Jeśli użyjesz tej funkcji, wszystko inne w module, takie jak globalne, inne funkcje itp. Zostanie utracone po sys.modules
przypisaniu - więc upewnij się, że wszystko, co potrzebne, znajduje się w klasie zastępczej.
Uwaga 2 : Aby wspierać from module import *
, musisz __all__
zdefiniować w klasie; na przykład:
class Foo:
def funct1(self, <args>): <code>
def funct2(self, <args>): <code>
__all__ = list(set(vars().keys()) - {'__module__', '__qualname__'})
W zależności od wersji Pythona mogą istnieć inne nazwy do pominięcia __all__
. set()
Można pominąć, jeżeli Python 2 kompatybilność nie jest potrzebne.