Introspecção de funções

classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

Introspecção de funções

Luciano Ramalho
Ao introspectar uma função, queria saber como associar os valores
default aos argumentos declarados. Vejam o código:

http://pastebin.com/m5d5912ef

#######################################
x = 99

def funcao(a, b=2, c='xyz'):
   z = 77
   return '%s, %s, %s, %s' % (x, a, b, c)

for atr in dir(funcao):
    if atr.startswith('func_'):
        print '%14s: %r' % (atr, getattr(funcao,atr))

cod = funcao.func_code

for atr in dir(cod):
    if atr.startswith('co_'):
        print '%14s: %r' % (atr, getattr(cod,atr))
#######################################

Isso produz a saída abaixo (melhor ver no pastebin):

http://pastebin.com/m324f49e6

#######################################
  func_closure: None
     func_code: <code object funcao at 0x7f44f5577738, file
"objeto_funcao.py", line 3>
 func_defaults: (2, 'xyz')
     func_dict: {}
      func_doc: None
  func_globals: {'__builtins__': <module '__builtin__' (built-in)>,
'__file__': 'objeto_funcao.py', 'funcao': <function funcao at
0x7f44f5571b18>, 'atr': 'func_globals', 'x': 99, '__name__':
'__main__', '__doc__': None}
     func_name: 'funcao'
   co_argcount: 3
   co_cellvars: ()
       co_code:
'd\x01\x00}\x03\x00d\x02\x00t\x00\x00|\x00\x00|\x01\x00|\x02\x00f\x04\x00\x16S'
     co_consts: (None, 77, '%s, %s, %s, %s')
   co_filename: 'objeto_funcao.py'
co_firstlineno: 3
      co_flags: 67
   co_freevars: ()
     co_lnotab: '\x00\x01\x06\x01'
       co_name: 'funcao'
      co_names: ('x',)
    co_nlocals: 4
  co_stacksize: 5
   co_varnames: ('a', 'b', 'c', 'z')

#######################################

Sobre os argumentos da função, temos

co_argcount: 3
co_varnames: ('a', 'b', 'c', 'z')
func_defaults: (2, 'xyz')

Ou seja, eu consigo saber que a função tem 3 argumentos declarados, e
que eles se chamam 'a',  'b' e 'c' (o 'z' é uma variável local), e sei
que existem os valores default 2 e 'xyz', mas como saber qual default
pertence a qual argumento?

Bom, ao escrever a pergunta me ocorreu a resposta: basta alinhar a
tupla func_defaults pelo final da tupla de args, porque todos os args
com default vem depois dos args sem default, óbvio.

Ficou assim, então (duas últimas linhas):

http://pastebin.com/d601a1ec9

###
arg_defaults = cod.co_varnames[cod.co_argcount-len(funcao.func_defaults):cod.co_argcount]
print 'args com default: ', zip(arg_defaults, funcao.func_defaults)
###

Final da saída:

###
args com default:  [('b', 2), ('c', 'xyz')
###

Valeu, pessoal, é sempre bom ter com quem "falar" para organizar o pensamento!

[ ]s
Luciano
Reply | Threaded
Open this post in threaded view
|

Re: Introspecção de funções

Pedro Werneck-2
On Sunday 11 January 2009 19:51:47 Luciano Ramalho wrote:
> Ao introspectar uma função, queria saber como associar os valores
> default aos argumentos declarados. Vejam o código:
[corta]
>
> Ou seja, eu consigo saber que a função tem 3 argumentos declarados, e
> que eles se chamam 'a',  'b' e 'c' (o 'z' é uma variável local), e sei
> que existem os valores default 2 e 'xyz', mas como saber qual default
> pertence a qual argumento?
>
> Bom, ao escrever a pergunta me ocorreu a resposta: basta alinhar a
> tupla func_defaults pelo final da tupla de args, porque todos os args
> com default vem depois dos args sem default, óbvio.

Interessante... ao ler você descrevendo o método eu pensei que quebraria ao
usar *args e **kwds e até escrevi o código conferindo antes e alterando a
contagem, mas não estava funcionando. Aí que percebi que eles não afetam o
número de argumentos em co_argcount... Python sempre me surpreende. :)

[corta]
> Valeu, pessoal, é sempre bom ter com quem "falar" para organizar o
> pensamento!

Bom... um detalhe que eu pensei é verificar func_defaults antes, porque se não
houver defaults ele será None e a chamada a len() vai dar erro.


--
Pedro Werneck
Reply | Threaded
Open this post in threaded view
|

Re: Introspecção de funções

Guilherme Polo
In reply to this post by Luciano Ramalho
2009/1/11 Luciano Ramalho <[hidden email]>:

> .
> .
> Ficou assim, então (duas últimas linhas):
>
> http://pastebin.com/d601a1ec9
>
> ###
> arg_defaults =
> cod.co_varnames[cod.co_argcount-len(funcao.func_defaults):cod.co_argcount]
> print 'args com default: ', zip(arg_defaults, funcao.func_defaults)
> ###
>

Ou você pode usar o inspect:

import inspect

argspec = inspect.getargspec(funcao)
args, defaults = argspec[0], argspec[-1]

print zip(args[::-1], defaults[::-1])

> Final da saída:
>
> ###
> args com default: [('b', 2), ('c', 'xyz')
> ###
>
> Valeu, pessoal, é sempre bom ter com quem "falar" para organizar o
> pensamento!
>
> [ ]s
> Luciano
>


--
-- Guilherme H. Polo Goncalves
Reply | Threaded
Open this post in threaded view
|

Re: Introspecção de funções

Guilherme Polo
In reply to this post by Pedro Werneck-2
2009/1/11 Pedro Werneck <[hidden email]>:

> On Sunday 11 January 2009 19:51:47 Luciano Ramalho wrote:
>> Ao introspectar uma função, queria saber como associar os valores
>> default aos argumentos declarados. Vejam o código:
> [corta]
>>
>> Ou seja, eu consigo saber que a função tem 3 argumentos declarados, e
>> que eles se chamam 'a', 'b' e 'c' (o 'z' é uma variável local), e sei
>> que existem os valores default 2 e 'xyz', mas como saber qual default
>> pertence a qual argumento?
>>
>> Bom, ao escrever a pergunta me ocorreu a resposta: basta alinhar a
>> tupla func_defaults pelo final da tupla de args, porque todos os args
>> com default vem depois dos args sem default, óbvio.
>
> Interessante... ao ler você descrevendo o método eu pensei que quebraria ao
> usar *args e **kwds e até escrevi o código conferindo antes e alterando a
> contagem, mas não estava funcionando. Aí que percebi que eles não afetam o
> número de argumentos em co_argcount... Python sempre me surpreende. :)
>

Bem, no Python 3 afeta mas é óbvio que ele não foi levado em consideração.


--
-- Guilherme H. Polo Goncalves
Reply | Threaded
Open this post in threaded view
|

Re: Introspecção de funções

Luciano Ramalho
In reply to this post by Guilherme Polo
2009/1/11 Guilherme Polo <[hidden email]>:
> Ou você pode usar o inspect:
>
> import inspect
>
> argspec = inspect.getargspec(funcao)
> args, defaults = argspec[0], argspec[-1]
>
> print zip(args[::-1], defaults[::-1])

Valeu, Guilherme, eu não conhecia o módulo inspect, grande dica!

[ ]s
Luciano
Reply | Threaded
Open this post in threaded view
|

Re: Introspecção de funções

Luciano Ramalho
In reply to this post by Pedro Werneck-2
2009/1/11 Pedro Werneck <[hidden email]>:
> Interessante... ao ler você descrevendo o método eu pensei que quebraria ao
> usar *args e **kwds e até escrevi o código conferindo antes e alterando a
> contagem, mas não estava funcionando. Aí que percebi que eles não afetam o
> número de argumentos em co_argcount... Python sempre me surpreende. :)

Hmm, vou ter que fazer mais uns testes para entender as consquencias
de usar parâmetros *args e **kwargs...

Bem lembrado!

> Bom... um detalhe que eu pensei é verificar func_defaults antes, porque se não
> houver defaults ele será None e a chamada a len() vai dar erro.

Certo, valeu pelos toques, Werneck!

[ ]s
Luciano