Python как создать метод для метода динамически?

Приветствую.
Подскажите вариант динамического создания методов для методов и для метода, .... :)
Создаю класс который будет обрабатывать несуществующий метод, возвращая название метода и переданные ему аргументы:
class A():
    def __getattr__(self, m):
        def wrap(*args):
            if not args: args = ""
            return "%s %s" % (m, " ".join(args))
        return wrap

Т.е.
>>> a = A():
>>> a.method(1,2,3)
method 1 2 3
>>> a.ufo('is', 'not', 'true')
ufo is not true

Для реализации дополнительного метода, вида:
>>> a.method(1,2,3).submethod('ufo', 'is', 'not', 'true')

можно написать так:
class A():
    def __getattr__(self, m):
        def wrap(*args):
            if not args: args = ""
            return _A("%s %s" % (m, " ".join(args)))
        return wrap


class _A():
    def __init__(self, s):
        self.s = s
    def __getattr__(self, m):
        def wrap(*args):
            if not args: args = ""
            else:
                w = " %s %s" % (m, " ".join(args))
            return self.s + w
        return wrap

Недавно я столкнулся с peewee и там, как положено ORM, подобных методов может быть много:
Entry.select().join().where().order_by()
Решил посмотреть в исходниках как это реализовано. Там же все методы обрабатываются рекурсивно, в общем подобное реализовать не получилось, немного мозги даже закипели.
Может кто подскажет простой вариант реализации рекурсивной обработки методов.
  • Вопрос задан
  • 1624 просмотра
Решения вопроса 1
@Maks113
В peewee скорее всего реализовано так:
class Entry(object):
	def select(self):
		print('do select')
		return self

	def join(self):
		print('do join')
		return self

	def where(self):
		print('do where')
		return self

	def order_by(self):
		print('do order_by')
		return self

>>> entry = Entry()
>>> entry.select().join().where().order_by()
do select
do join
do where
do order_by
<Entry object at ...>


Если использовать только как затычку, то можно извратиться так:
class A(object):
	def __init__(self):
		self.stack = []

	def __getattr__(self, name):
		def wrap(*args, **kwargs):
			self.stack.append('name: {} args: {} kwargs: {}'
								.format(name, args, kwargs))
			return self
		return wrap

	def __repr__(self):
		for call in self.stack:
			print(call)
		self.stack = []
		return super().__repr__()

>>> a = A()
>>> a.foo(123, func='321')
name: foo args: (123,) kwargs: {'func': '321'}
<A object at ...>

>>> a.foo(123, func='321').bar(111).baz('string')
name: foo args: (123,) kwargs: {'func': '321'}
name: bar args: (111,) kwargs: {}
name: bazz args: ('string',) kwargs: {}
<A object at ...>
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
ckpunT
@ckpunT Автор вопроса
Немного развил свою мысль.
def clause(func):
    def func_wrapper(*args, **kwargs):
        operation = " " + func.__name__.replace('_', '') + " "
        if args:
            if len(args) == 1:
                a = operation + "".join(str(i) for i in args)
            else:
                a = "(" + operation.join(str(i) for i in args) + ")"
        else:
            a = ""
        if kwargs:
            clause = []
            for k, v in kwargs.items():
                op = '='
                if str(v).find('%') >= 0:
                    op = 'like'
                clause.append("%s %s '%s'" % (k, op, v))
            if len(kwargs) == 1:
                k = operation + "".join(clause)
            else:
                k = "(" + operation.join(clause) + ")"
        else:
            k = ""
        return a + k
    return func_wrapper

class ORM():
    def __init__(self, table):
        self.table = table
    def select(self, *args):
        if args:
            list_cols = ", ".join(args)
        else:
            list_cols = "*"
        self.sql = "select %s from %s" % (list_cols, self.table)
        return self
    def insert(self, **kwargs):
        if not kwargs:
            raise
        else:
            col = ", ".join(kwargs.keys())
            val = ", ".join(["'{}'"] * len(kwargs))
            sql = "insert into %s (%s) values (%s)" % (self.table, col, val)
            self.sql = sql.format(*kwargs.values())
            return self
    def update(self, **kwargs):
        if not kwargs:
            raise
        else:
            upd = ", ".join(["%s='%s'" % (k, v) for k, v in kwargs.items()])
            self.sql = "update %s set %s" % (self.table, upd)
            return self
    def delete(self):
        self.sql = "delete from %s" % (self.table)
        return self
    def __getattr__(self, name):
        def wrap(*args, **kwargs):
            exp = " " + name.replace('_', ' ') + " "
            if args:
                self.sql = self.sql + exp + ", ".join(str(i) for i in args)
            elif kwargs:
                clause = []
                for k, v in kwargs.items():
                    op = '='
                    if str(v).find('%') >= 0:
                        op = 'like'
                    clause.append("%s %s '%s'" % (k, op, v))
                self.sql = self.sql + exp + " ".join(clause)
            return self
        return wrap
    def __repr__(self):
        s = self.sql[:]
        self.sql = ''
        return s


@clause
def _and(*args, **kwargs):
    pass

@clause
def _or(*args, **kwargs):
    pass

def _in(**kwargs):
    for k, v in kwargs.items():
        return k + " in (" + ", ".join(str(i) for i in v) + ")"

>>> t = ORM('test')
>>> t.select('id', 'count(id) as cid', 'name', 'age').where(_or(_and(name='alex%', age=25), _and(name='%bob', age='27'), _in(age=range(25,35)))).group_by('cid', 'name', 'age').order_by('cid', 'name', 'age').limit(10)
select id, count(id) as cid, name, age from test where ((age = '25' and name like 'alex%') or (age = '27' and name like '%bob') or age in (25, 26, 27, 28, 29, 30, 31, 32, 33, 34)) group by cid, name, age order by cid, name, age limit 10
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы