Тут зіткнулися дві основні проблеми:
__xxx__
методи шукають лише на уроці
TypeError: can't set attributes of built-in/extension type 'module'
(1) означає, що будь-яке рішення повинно також слідкувати за тим, який модуль досліджувався, інакше кожен модуль матиме поведінку підстановки примірника; і (2) означає, що (1) навіть неможливо ... принаймні не безпосередньо.
На щастя, sys.modules не є вибагливим щодо того, що відбувається там, щоб обгортка працювала, але тільки для доступу до модулів (тобто import somemodule; somemodule.salutation('world')
; для доступу до цього ж модуля вам досить доведеться вибирати методи з класу заміни та додавати їх у globals()
eiher за допомогою користувальницький метод у класі (мені подобається використовувати .export()
) або із загальною функцією (наприклад, ті, що вже вказані як відповіді). Одне, що потрібно пам’ятати: якщо обгортка створює новий екземпляр кожного разу, а рішення глобальних програм - ні, Ви закінчуєте тонко різну поведінку. О, і вам не вдається використовувати обидва одночасно - це те чи інше.
Оновлення
Від Гідо ван Россума :
Насправді є хак, який періодично використовується та рекомендується: модуль може визначити клас із потрібною функціональністю, а потім, наприкінці, замінити себе в sys.modules екземпляром цього класу (або класом, якщо ви наполягаєте , але це взагалі менш корисно). Наприклад:
# module foo.py
import sys
class Foo:
def funct1(self, <args>): <code>
def funct2(self, <args>): <code>
sys.modules[__name__] = Foo()
Це працює, тому що імпортна техніка активно вмикає цей злом, і як його останній крок витягує фактичний модуль із sys.modules після його завантаження. (Це не випадково. Злом був запропонований давно, і ми вирішили, що нам сподобалося достатньо, щоб підтримати його в імпортній техніці.)
Таким чином, встановлений спосіб досягти того, що ви хочете, - це створити єдиний клас у своєму модулі, і як останній акт модуля замініть sys.modules[__name__]
на екземпляр вашого класу - і тепер ви можете грати з __getattr__
/ __setattr__
/ __getattribute__
за потребою.
Примітка 1. Якщо ви користуєтесь цією функціональністю, то все інше в модулі, наприклад глобалі, інші функції тощо, буде втрачено під час sys.modules
виконання завдання, тому переконайтеся, що все необхідне знаходиться в класі заміщення.
Примітка 2 : Для підтримки from module import *
ви повинні __all__
визначитись у класі; наприклад:
class Foo:
def funct1(self, <args>): <code>
def funct2(self, <args>): <code>
__all__ = list(set(vars().keys()) - {'__module__', '__qualname__'})
Залежно від вашої версії Python, можуть бути інші імена, які слід опустити __all__
. set()
Може бути опущена , якщо сумісність Python 2 не потрібно.