Как может реализовать эквивалент a __getattr__
на классе, на модуле?
При вызывании функции, которая не существует в статически определенных атрибутах модуля, я хочу создать экземпляр класса в том модуле и вызвать метод на него с тем же именем, как отказавший в поиске атрибута на модуле.
class A(object):
def salutation(self, accusative):
print "hello", accusative
# note this function is intentionally on the module, and not the class above
def __getattr__(mod, name):
return getattr(A(), name)
if __name__ == "__main__":
# i hope here to have my __getattr__ function above invoked, since
# salutation does not exist in the current namespace
salutation("world")
Который дает:
matt@stanley:~/Desktop$ python getattrmod.py
Traceback (most recent call last):
File "getattrmod.py", line 9, in <module>
salutation("world")
NameError: name 'salutation' is not defined
Обычно мы так не поступаем.
Мы делаем вот что.
class A(object):
....
# The implicit global instance
a= A()
def salutation( *arg, **kw ):
a.salutation( *arg, **kw )
Почему? Чтобы неявный глобальный экземпляр был виден.
В качестве примеров посмотрите на модуль random
, который создает неявный глобальный экземпляр, чтобы немного упростить варианты использования, в которых вам нужен «простой» генератор случайных чисел.
Подобно тому, что предлагал @ Håvard S, в случае, когда мне нужно было реализовать некоторую магию в модуле (например, __ getattr __
), я бы определил новый класс, который наследуется от типов.
и поместите его в sys.modules
(вероятно, заменив модуль, в котором был определен мой пользовательский ModuleType
).
См. Основной файл __ init __. Py
в Werkzeug для достаточно надежной реализации этого.
Создайте файл модуля с вашими классами. Импортируйте модуль. Запустите getattr
для только что импортированного модуля. Вы можете выполнить динамический импорт с помощью __ import __
и извлечь модуль из sys.modules.
Вот ваш модуль some_module.py
:
class Foo(object):
pass
class Bar(object):
pass
И в другом модуле:
import some_module
Foo = getattr(some_module, 'Foo')
Выполнение этого динамически:
import sys
__import__('some_module')
mod = sys.modules['some_module']
Foo = getattr(mod, 'Foo')
Это взлом, но вы можете обернуть модуль классом:
class Wrapper(object):
def __init__(self, wrapped):
self.wrapped = wrapped
def __getattr__(self, name):
# Perform custom logic here
try:
return getattr(self.wrapped, name)
except AttributeError:
return 'default' # Some sensible default
sys.modules[__name__] = Wrapper(sys.modules[__name__])
Это халтура, но...
import types
class A(object):
def salutation(self, accusative):
print "hello", accusative
def farewell(self, greeting, accusative):
print greeting, accusative
def AddGlobalAttribute(classname, methodname):
print "Adding " + classname + "." + methodname + "()"
def genericFunction(*args):
return globals()[classname]().__getattribute__(methodname)(*args)
globals()[methodname] = genericFunction
# set up the global namespace
x = 0 # X and Y are here to add them implicitly to globals, so
y = 0 # globals does not change as we iterate over it.
toAdd = []
def isCallableMethod(classname, methodname):
someclass = globals()[classname]()
something = someclass.__getattribute__(methodname)
return callable(something)
for x in globals():
print "Looking at", x
if isinstance(globals()[x], (types.ClassType, type)):
print "Found Class:", x
for y in dir(globals()[x]):
if y.find("__") == -1: # hack to ignore default methods
if isCallableMethod(x,y):
if y not in globals(): # don't override existing global names
toAdd.append((x,y))
for x in toAdd:
AddGlobalAttribute(*x)
if __name__ == "__main__":
salutation("world")
farewell("goodbye", "world")
Это работает путем итерации по всем объектам в глобальном пространстве имен. Если элемент является классом, он переиртуется по атрибутам класса. Если атрибут вызывается, он добавляет его в глобальное пространство имен в качестве функции.
Он игнорирует все атрибуты, содержащие "__".
Я бы не стал использовать это в производственном коде, но это должно поставить вас на работу.