def fun(arg0, arg1, arg2, arg3): pass
y no
def fun(*args): pass
ya que la API de dbus usa inspect para determinar el numero de argumentos posicionales
me tomo varias horas sacarlo, buscaba y buscaba y cada vez iba mas a bajo nivel. Hasta que llegue al mas bajo nivel que se puede llegar... generar bytecodes :)
y bue, pelee con eso un buen rato ya que si bien alguna vez jugue haciendo maquinas virtuales de juguete esta era la primera vez contra una en serio.
el resultado es el siguiente:
import dis
import types
def gen(num):
op = dis.opmap.__getitem__
ops = [op('LOAD_GLOBAL'), 0, 0]
for argnum in range(num):
ops.append(op('LOAD_FAST'))
ops.append(argnum)
ops.append(0)
ops.append(op('CALL_FUNCTION'))
ops.append(num)
ops.append(0)
ops.append(op('RETURN_VALUE'))
code_str = ''.join(chr(x) for x in ops)
return code_str
def gen_code(num, global_vars, name='func', filename='magic.py'):
code = gen(num)
varnames = ['arg' + str(x) for x in range(num)]
names = global_vars + varnames
names = tuple(names)
varnames = tuple(varnames)
return types.CodeType(num, num, num, 0, code, (), names, varnames, filename, name, 1, '')
def gen_fun(num, name, func):
code = gen_code(num, [func.__name__], name)
return types.FunctionType(code,{func.__name__: func})
if __name__ == '__main__':
def printer(*args):
print args
f4 = gen_fun(4, 'f4', printer)
dis.dis(f4.func_code)
f4(1,2,3,4)
f1 = gen_fun(1, 'f1', printer)
dis.dis(f1.func_code)
f1('only one arg')
try:
f1(1, 2)
except TypeError:
print 'ok, ok, only one argument'
lo que hace es generar una funcion que recibe N parametros y que lo unico que hace es llamar a otra funcion pasandole esos parametros, lo cual seria algo como:
def fun1(*args): print args
def fun_que_recibe_4_parametros(a,b,c,d): fun1(a,b,c,d)
con lo que si escribimos nuestra funcion en fun1 y creamos las funciones que reciben los distintos parametros con un for tenemos lo que necesitamos :)
la salida de la ejecusion de lo de arriba es:
1 0 LOAD_GLOBAL 0 (printer)
3 LOAD_FAST 0 (arg0)
6 LOAD_FAST 1 (arg1)
9 LOAD_FAST 2 (arg2)
12 LOAD_FAST 3 (arg3)
15 CALL_FUNCTION 4
18 RETURN_VALUE
(1, 2, 3, 4)
1 0 LOAD_GLOBAL 0 (printer)
3 LOAD_FAST 0 (arg0)
6 CALL_FUNCTION 1
9 RETURN_VALUE
('only one arg',)
ok, ok, only one argument
fuentes de inspiracion:
http://pyref.infogami.com/type-code
http://docs.python.org/library/dis.html
http://docs.python.org/library/types
http://docs.python.org/reference/datamodel.html#the-standard-type-hierarchy (la parte de callable types)
5 comentarios:
Se podría haber usado decoradores para lograr lo mismo?
Cómo así:
http://codepad.org/J1nFBej9.
(comentame algo, disculpá si no era la idea, pero escribí esto volando antes de irme a una fiesta).
http://us.php.net/manual/en/function.func-get-args.php
http://us.php.net/manual/en/function.create-function.php
hacelo en php buacho
@Juanjo: no es lo mismo, lo que explico en el post es que necesito que la funcion tenga exactamente ese numero de argumentos porque el modulo de dbus usa inspect para saber cuantos argumentos recibe la funcion, y si no es la cantidad especificada en el decorador no te deja usar la funcion.
Por lo que no puedo usar *args, lo podes ver bien aca http://cgit.freedesktop.org/dbus/dbus-python/tree/dbus/decorators.py#n327
@Nassto: eso casi como eval (feo) y ademas eso se puede hacer de una forma mucho mas simple en python :D
¡Groso!
¿Seguro que con type() no lo podías hacer, no? (descarto lambda también...).
Interesante lo del types.FunctionType(code, ...) :-)
Salutes
Publicar un comentario