Модераторы: Daevaorn
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Динамический вызов класса 
:(
    Опции темы
WolfAlone
Дата 23.12.2010, 17:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


В экстазе
***


Профиль
Группа: Завсегдатай
Сообщений: 1010
Регистрация: 16.9.2008
Где: Рай

Репутация: нет
Всего: 5



Доброго времени суток!

Есть некий класс, например class1, у него есть метод: method1, который принимает 1 или более параметров (param1, ... paramX). Метод возвращает число или строку (не принципиально).

Есть список, list1, со следующим содержимым (по индексу):
0 - имя класса;
1 - имя метода;
2 - * параметр1, 2, 3 и т.д.

Необходимо, обработать список и на основе его содержимого обратиться к указанному в нём классу, затем методу и передать ему параметры. Результат работы - вернуть в вызывающую функцию.

Объясню суть вопроса.
В PHP для подобных целей есть, например функция: __autoload(), которая позволяет подгружать файл, в котором содержится объявление необходимого класса. Имя класса и его метод могут содержаться в переменной в виде строки. Параметры в функцию или метод класса можно передавать в виде массива, при этом, каждый элемент массива будет равен 1-му параметру функции/метода.

Хотелось бы узнать, как сделать подобное в Python?


--------------------
И сказал Бог: "Тогда я построю свой мир с блэк-джеком и шлюхами!"

Ф топку Ubuntu, Debian наше фсё!

(с) Евгений Вольф
PM MAIL WWW ICQ Skype   Вверх
skyboy
Дата 30.12.2010, 00:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


неОпытный
****


Профиль
Группа: Модератор
Сообщений: 9820
Регистрация: 18.5.2006
Где: Днепропетровск

Репутация: нет
Всего: 260



Цитата(WolfAlone @  23.12.2010,  16:07 Найти цитируемый пост)
В PHP для подобных целей есть

call_user_func_array есть в PHP
а для Python, как я понял. можно использовать getattr

Добавлено через 1 минуту и 32 секунды
а вот как передать массив параметров, как совокупность - не знаю.
PM MAIL   Вверх
av0000
Дата 30.12.2010, 12:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 26
Регистрация: 11.2.2009

Репутация: 1
Всего: 2



Ну, например: как-то так (без проверок на ошибки)

Код

import my_module

for x in list1:
  cls = getattr(my_module, x[0]) # класс
  c = cls() # экземпляр класса
  m = getattr(c, x[1]) # метод

  ret = m(*x[2:]) # остатки массива как развёрнутый список параметров, для именованных (dict) - **{}


UPD: Для динамической загрузке модулей смотри в сторону imp
Вот кусок из моего проектика, где загружаются плагины, так, для иллюстрации:
Код

    def load_plugins(self):
        """Enumerate and load plugin modules without creation instances."""

        dirs = [os.path.join(config.APP_PATH, config.DIR_PLUGINS)]
        s = config.ini.value("pluginspath").toString()
        if s:
            #dirs.extend([os.path.join(unicode(s),x) for x in os.listdir(s)])
            dirs.append(unicode(s))
        plgs = []
        for d in set(dirs): # set prevents double-includes
            for x in os.listdir(d):
                dd = os.path.join(d, x)
                if os.path.isdir(dd):
                    try:
                        f = imp.find_module(x, [d])
                    except:
                        self.log.debug("Non-plugin folder in plugins tree: '%s'" % (dd))
                        f = None
                    if f:
                        self.log.debug("Loading plugin '%s' from '%s'..." % (x, dd))
                        try:
                            m = imp.load_module(x, *f)
                            self.log.debug("The plugin module '%s' loaded." % (x,))
                            self.plug_modules.append(m) # array of modules
                        except Exception as e:
                            self.log.exception("Error loading plugin '%s':\n%s" % (x, e))


    def create_plugins(self):
        """Enumerate plugin modules and create plugin classes.

        Should be done AFTER loading translations."""
        for m in self.plug_modules:
            for mm in dir(m):
                cl = getattr(m, mm)
                if inspect.isclass(cl) and issubclass(cl, base.BasePlugin):
                    self.log.debug("Create plugin instance %s:%s" % (m.__name__, mm))
                    self.addPlugin(cl) # exception-safe


Это сообщение отредактировал(а) av0000 - 30.12.2010, 12:56
PM MAIL Jabber   Вверх
bilbobagginz
Дата 7.1.2011, 18:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Naughtius Maximus
****


Профиль
Группа: Экс. модератор
Сообщений: 8813
Регистрация: 2.3.2004
Где: Israel

Репутация: 6
Всего: 317



Цитата(WolfAlone @  23.12.2010,  16:07 Найти цитируемый пост)
Хотелось бы узнать, как сделать подобное в Python? 

обычно так не работают в питоне, но если уж не в терпеж:
Код

class A:
   def func(self, a):
      print 'called to A::func(%s)' %a

class B:
   def funcb(self, b):
      print 'called to B::funcb(%s)' %b


l1 = [ 'A', 'func', 'par1', 'par2', 'parN']
l2 = [ 'B', 'funcb', 'par3', 'par5', 'parZ']

def test_dyncall(l):
   cn = eval(l[0]) # находим имя класса А
   args = l[2:] # список параметров
   b = cn() #создаем объект класса А
   b.fn = eval("b.%s" %l[1]) # определим метод по имени fn
   for i in args:
      b.fn(i) # de facto вызовы A.func(i) по указателю fn.

for l in (l1, l2):
   test_dyncall(l)


ессно надо протестить на всякие бяки....




Это сообщение отредактировал(а) bilbobagginz - 8.1.2011, 01:34


--------------------
Я ещё не демон. Я только учусь.
PM WWW   Вверх
WolfAlone
Дата 18.1.2011, 12:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


В экстазе
***


Профиль
Группа: Завсегдатай
Сообщений: 1010
Регистрация: 16.9.2008
Где: Рай

Репутация: нет
Всего: 5



Цитата(bilbobagginz @  7.1.2011,  18:48 Найти цитируемый пост)
обычно так не работают в питоне, но если уж не в терпеж:

Подскажите пожалуйста, как правильно решать подобную задачу?

В своё время работал с такой штукой на PHP как CodeIgniter, там очень удобно прасится URL. Выглядит это примерно так:
example.com/class/func/param1/param2/param3/etc

где:
example.com - адрес сайта
class - пользовательский контроллер (класс)
func - функция в этом классе (метод класса)
param 1-3 (всё остальное) - входные параметры для этой функции

Задумка мне очень понравилась, решил сделать нечто аналогичное на Python...


--------------------
И сказал Бог: "Тогда я построю свой мир с блэк-джеком и шлюхами!"

Ф топку Ubuntu, Debian наше фсё!

(с) Евгений Вольф
PM MAIL WWW ICQ Skype   Вверх
WolfAlone
Дата 19.1.2011, 22:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


В экстазе
***


Профиль
Группа: Завсегдатай
Сообщений: 1010
Регистрация: 16.9.2008
Где: Рай

Репутация: нет
Всего: 5



Писал я тут писал... Много написал smile Как вдруг до меня стало доходить!
av0000, спасибо огромное, Вы натолкнули меня на верное направление!
bilbobagginz, премного благодарен! Из двух примеров выше сейчас буду пробовать собрать 1  smile 


--------------------
И сказал Бог: "Тогда я построю свой мир с блэк-джеком и шлюхами!"

Ф топку Ubuntu, Debian наше фсё!

(с) Евгений Вольф
PM MAIL WWW ICQ Skype   Вверх
WolfAlone
Дата 19.1.2011, 23:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


В экстазе
***


Профиль
Группа: Завсегдатай
Сообщений: 1010
Регистрация: 16.9.2008
Где: Рай

Репутация: нет
Всего: 5



Остался пожалуй только 1 вопрос:
Как защититься от "кул хацкеров", т.е. проще говоря как "найти" класс, зная его имя записанное в строковую переменную без eval() или как проверить, то ли (что нужно/можно) выполняется в eval(), что может выполняться? Если создать список с допустимыми значениями - это будет надёжной защитой?

Я немного поясню:
Код

s = 'class1'
c = eval(s)
z = c() #Этот код прекрасно работает, именно так - как я хотел.
#Проблема заключается только в том, что s - в скрипт передаёт пользователь, и соответственно, передать он суда может всё, что угодно!



--------------------
И сказал Бог: "Тогда я построю свой мир с блэк-джеком и шлюхами!"

Ф топку Ubuntu, Debian наше фсё!

(с) Евгений Вольф
PM MAIL WWW ICQ Skype   Вверх
av0000
Дата 20.1.2011, 11:33 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 26
Регистрация: 11.2.2009

Репутация: 1
Всего: 2



Ну, во-первых я сильно не люблю пользоваться eval по двум причинам - выжирание памяти "как под целый питон" на время запуска (можно сказать, что на вызов eval "запускается" новый интерпретатор) и как раз эта дыра с запуском произвольного кода.

решение "раз" - сделать словарь с именами классов и классами и искать допустимые только в нем:
Код

AAA = {
    'class1': Class1, # 
    'class2': MySuperPuperClass,
}
# юзер вводит класс
s = rawinput()
cls = AAA.get(s, None)
if cls:
   instance = cls()


решение "два" если охота таки использовать eval:
Код

try:
   inst = eval(s + '()', {}, {})
except:
   pass # как-то обрабатываем

"фишка" второго решения - во-первых принудительно создаём экземпляр внутри eval - какую-то часть "мусора" удастся этим отсечь, во-вторых - ограничиваем список того, чем может пользоваться eval (см. доку на её параметры) НО! нормального сандбокса таким ограничением не сделать :( и при желании юзер может ввести что-то типа "import os; os.system('rm -rf /')" и кому-то будет очень весело ;)

PS: прошу не воспринимать строку с import буквально smile прям так, конечно никто сделать не даст, но обойти "защиту" в виде {} можно, такая инфа мне попадалась в и-нете, так что стОит быть аккуратным
PPS: а заполнять словарь из первого примера можно по аналогии с тему куском кода, что я привёл раньше
Код

import mmm
import inspect
AAA = {}
for x in dir(mmm):
    a = getattr(mmm, x)
    if inspect.isclass(a):
        AAA[a.__name__] = a



Это сообщение отредактировал(а) av0000 - 20.1.2011, 11:37
PM MAIL Jabber   Вверх
stalk13
Дата 14.5.2011, 16:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 3
Регистрация: 14.5.2011

Репутация: нет
Всего: нет



Я бы в этом случае использовал декораторы:

Код

"""
Examples:

>>> @controller
... class default:
...
...     @action
...     def index(self, arg1=100):
...         return arg1
...
...     @action
...     def other_action(self, arg1, arg2='test'):
...         return 'Other action: %s, %s' % (arg1, arg2)
...
...     def no_action_method(self, *args):
...         pass

>>> route('default', 'index')
100

>>> params = ['default', 'other_action', 'foo', 'bar']
>>> route(*params)
'Other action: foo, bar'

>>> params = 'default/no_action_method/param1'.split('/')
>>> route(*params)
Traceback (most recent call last):
    ...
RuntimeError: Action "no_action_method" of controller "default" is not found
"""

def route(controller, action, *args):
    if not controller in route.controllers:
        raise RuntimeError('Controller "%s" is not found' % controller)

    cls = route.controllers[controller]()

    if not action in cls._actions:
        raise RuntimeError('Action "%s" of controller "%s" is not found' % (action, controller))

    return cls._actions[action](cls, *args)

def controller(cls):
    """ Class decorator """

    cls._actions = controller.actions
    controller.actions = {}
    route.controllers[cls.__name__] = cls
    return cls

def action(func):
    """ Method decorator """

    controller.actions[func.__name__] = func
    return func

route.controllers = {}
controller.actions = {}

if __name__ == '__main__':
    import doctest
    doctest.testmod()


Это сообщение отредактировал(а) stalk13 - 16.5.2011, 09:14
PM MAIL   Вверх
bilbobagginz
Дата 15.5.2011, 07:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Naughtius Maximus
****


Профиль
Группа: Экс. модератор
Сообщений: 8813
Регистрация: 2.3.2004
Где: Israel

Репутация: 6
Всего: 317



согласен, что eval ресурсы хавает.
но:
Цитата(av0000 @  20.1.2011,  10:33 Найти цитируемый пост)
и при желании юзер может ввести что-то типа "import os; os.system('rm -rf /')"

не напугал. при этом желании юзер должен стать root-ом. ну или вылезти из jail. что не так уж и просто...



--------------------
Я ещё не демон. Я только учусь.
PM WWW   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Python: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0931 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.