Форум сайта python.su
Пытаюсь использовать вот этот рецепт: http://code.activestate.com/recipes/473888/
Работал на ура, пока не столкнулся с gtk.
Вот тестовый пример:
import importer
importer.install()
import gtk
print gtk.POS_LEFT
Traceback (most recent call last):
File "test.py", line 5, in <module>
print gtk.POS_LEFT
File "/usr/lib/python2.5/site-packages/importer.py", line 76, in __getattr__
mod = _loadModule()
File "/usr/lib/python2.5/site-packages/importer.py", line 34, in _loadModule
mod = imp.load_module(name, file, pathname, desc)
File "/usr/lib/python2.5/site-packages/gtk-2.0/gtk/__init__.py", line 50, in <module>
import gdk
ImportError: No module named gdk
Офлайн
Принтов напихай. На первый взгляд все должно работать (если gdk ставить в skip_list, конечно).
Офлайн
Ага, спасибо.
Я именно таким путем и пошел. Кроме того немного переделал, почитав это: http://blog.doughellmann.com/2009/11/pymotw-sys-part-7-modules-and-imports.html?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+PyMOTW+%28Python+Module+of+the+Week%29
Засада оказалось в cairo. __skip = оказалось достаточно для моего случая.
Правда в результате получилось не совсем то, что я ожидал, поэтому продолжаю копать.
Конструкции вида from bla.bla1 import bla2 вызывают __getattr__(“__path__”) для bla.bla1 и вся ленивость на этом заканчивается. Это происходит не во всех случаях, например from os.path import join этого не делает, а для моих модулей это происходит. Вот пытаюсь понять в чем разница.
Отредактировано (Дек. 6, 2009 12:13:55)
Офлайн
А никто счастья не обещал :)
os и os.path - это не package а обманка. Так что не показатель.
__path__ пусть себе дергают - при использованиее lazy importer нужно __init__.py делать пустыми (или очень маленькими).
Тогда много вреда не будет.
Еще стоит везде проставить from __future__ import absolute_import - уйдут лишние попытки открыть несуществующий файл, и таких много.
Офлайн
Да тут не до счастья, хоть какую-то пользу извлечь бы :)
С os все оказалось просто - он уже проимпортирован самим importer-ом до того, как тот регистрирует свой функционал, поэтому ничего и не звалось.
Так что чуда опять не случилось - все детерминированно и скучно, как обычно :)
Конструкция from popen2 import popen2 приводит к такой последовательности вызовов:
find_module popen2 None
__getattr__ <moduleProxy ‘popen2’ from ‘/usr/lib/python2.5/popen2.py’> __path__
_loadModule /usr/lib/python2.5/popen2.py
mod: <module ‘popen2’ from ‘/usr/lib/python2.5/popen2.pyc’>
__getattr__ <moduleProxy ‘popen2’ from ‘/usr/lib/python2.5/popen2.py’> popen2
_loadModule /usr/lib/python2.5/popen2.py
mod: <module ‘popen2’ from ‘/usr/lib/python2.5/popen2.pyc’>
result: <function popen2 at 0xb7eabd14>
То есть даже в этом случае происходит загрузка самого модуля, а не __init__.py .
Какая же это ленивость. Вовсе не этого хотелось :(
from __future__ import absolute_import это хорошо, спасибо. К сожалению у меня другой случай.
Я хотел привнести ленивость со стороны, не изменяя ничего в существующей системе.
Пошел читать теорию: http://www.python.org/dev/peps/pep-0302/
Офлайн
Еще одну вешь забыл упомянуть - для lazy import нельзя использовать конструкцию from module import name
Именно потому что, как вы метко заметили, ленивость сразу же исчезает.
Смотрите:
>>> def f():
... from urllib.request import urlopen
...
>>> dis.dis(f)
2 0 LOAD_CONST 1 (0)
3 LOAD_CONST 2 (('urlopen',))
6 IMPORT_NAME 0 (urllib.request)
9 IMPORT_FROM 1 (urlopen)
12 STORE_FAST 0 (urlopen)
15 POP_TOP
16 LOAD_CONST 0 (None)
19 RETURN_VALUE
>>>
Отредактировано (Дек. 6, 2009 15:37:39)
Офлайн
А по каким параметром вы определяете ленивый опкод или нет?
Я вот смотрю на это:
In [1]: import dis
In [2]: def f():
...: import popen2
...:
...:
In [3]: dis.dis(f)
2 0 LOAD_CONST 1 (-1)
3 LOAD_CONST 0 (None)
6 IMPORT_NAME 0 (popen2)
9 STORE_FAST 0 (popen2)
12 LOAD_CONST 0 (None)
15 RETURN_VALUE
Офлайн
Давайте я не поленюсь и покажу декомпиляцию для модулей.
Дело в том, что dis хочет объект - и самый быстрый способ, если все пишешь на лету в консоли - это создать функцию. И рассматривать ее просто как контейнер.
Сейчас поправлюсь.
mod = """
import os
from os import close
def f():
fd = os.open("some file")
close(fd)
class A:
def f(self):
pass
"""
mod = compile(mod, '<module>', 'exec')
import dis
print ("Module")
dis.dis(mod)
d = {}
exec(mod, d, d)
print ("Function")
dis.dis(d['f'])
Module
- Импорт os и сохранение под тем же именем
2 0 LOAD_CONST 0 (0)
3 LOAD_CONST 1 (None)
6 IMPORT_NAME 0 (os)
9 STORE_NAME 0 (os)
- импорт из os функции close, и сохранение имени 'close' - про модуль os можем забыть
- на самом деле "правильный" импортер, конечно, положит модуль в sys.modules - но коду создания этого не видно
- ленивый импортер получит свой __getattribute__ уже здесь, сразу при загрузке модуля
3 12 LOAD_CONST 0 (0)
15 LOAD_CONST 2 (('close',))
18 IMPORT_NAME 0 (os)
21 IMPORT_FROM 1 (close)
24 STORE_NAME 1 (close)
27 POP_TOP
- сделать функцию, и запомнить ее под именем f. <code object f ...> - это код самой функции.
5 28 LOAD_CONST 3 (<code object f at 0x01B8C968, file "<module>", line 5>)
31 MAKE_FUNCTION 0
34 STORE_NAME 2 (f)
- сделать класс и тоже сохранить под именем. Вызываемая функция на самом деле что-то вроде return type('A', (), class_dict) - но это уже другая история.
9 37 LOAD_BUILD_CLASS
38 LOAD_CONST 4 (<code object A at 0x01C09260, file "<module>", line 9>)
41 MAKE_FUNCTION 0
44 LOAD_CONST 5 ('A')
47 CALL_FUNCTION 2
50 STORE_NAME 3 (A)
53 LOAD_CONST 1 (None)
56 RETURN_VALUE
- теперь вызовы импортированных объектов в f
Function
- затянуть объект с именем os
6 0 LOAD_GLOBAL 0 (os)
- спросить у него аттрибут open (здесь как раз и происходит вызов __getattribute__ у ленивого импортера)
3 LOAD_ATTR 1 (open)
- вызвать функцию open
6 LOAD_CONST 1 ('some file')
9 CALL_FUNCTION 1
12 STORE_FAST 0 (fd)
- а в случае from mod import name все проще:
- уже есть имя close, просто подгружаем его в стек
7 15 LOAD_GLOBAL 2 (close)
- и вызываем
18 LOAD_FAST 0 (fd)
21 CALL_FUNCTION 1
- оставшиеся три инструкции на самом деле означают 'return None'
24 POP_TOP
25 LOAD_CONST 0 (None)
28 RETURN_VALUE
Офлайн
Спасибо за подробное объяснение.
Я не мог понять в чем корень зла. Теперь понимаю, что в STORE_NAME 1 (close).
Даже если вернуть вместо этого некий proxy, то можно будет отследить вызовы, перекрыв __call__, но просто обращения к этой переменной ну никак уже не отследишь.
Офлайн
Правильно поняли
Офлайн