Essa é para você, que fica desesperado em ter que digitar aquele MALDITO 'self' no início de todos seus métodos. Seus problemas acabaram! Você jamais terá de se preocupar em digitar aqueles quatro caracteres na definição de método novamente. Claro que você terá de se preocupar com muitas outras coisas, mas isso é um mero detalhe.
Basta salvar o código abaixo em um módulo, importar a classe NoSelfType para seu módulo e definir a variável 'metaclass = NoSelfType' no início.
OBS: aos desavisados, esse código é uma brincadeira, feita para mostrar como retirar o self é algo muito mais complicado do que parece! Não vá utilizá-lo em código real!
Atendendo a pedidos, coloquei comentários explicativos...
1 #!/usr/bin/env python
2 # encoding: utf-8
3
4
5 import opcode
6 import types
7 import dis
8
9 locals().update(opcode.opmap)
10
11
12
13 def noself(func, inst):
14 # A maior parte da mágica está aqui nesta função..
15
16 # Ela receberá a função que define o método a ser
17 # modificado e a instância que deve aparecer lá dentro como 'self'
18
19 # o que ela faz é simplesmente colocar no início do método o
20 # equivalente à linha: self = inst
21
22 # primeiro, precisamos do código da função
23 code = func.func_code
24
25 # agora precisamos de tudo do objeto código, retirar
26 # todos os atributos para recriá-lo mais tarde... nem tudo aqui
27 # será alterado, mas facilita depois
28
29 argcount = code.co_argcount # número de parâmetros
30 nlocals = code.co_nlocals # número de variáveis locais
31 stacksize = code.co_stacksize # tamanho máximo da pilha
32 flags = code.co_flags # algumas flags para o interpretador
33 codestring = code.co_code # o código
34 constants = code.co_consts # as constantes na função.
35 names = code.co_names # nomes de globais usadas
36 varnames = code.co_varnames # nomes de variáveis usadas
37 filename = code.co_filename # nome do arquivo
38 name = code.co_name # nome da função
39 firstlineno = code.co_firstlineno # primeira linha da função
40 lnotab = code.co_lnotab
41 freevars = code.co_freevars # variáveis livres
42 cellvars = code.co_cellvars # variáveis usadas em função aninhada
43
44 # primeiro, colocamos o nome 'self' na lista de nomes de
45 # variáveis... colocamos no final para evitar ter de mudar o
46 # índice de todas as outras
47 varnames = varnames + ('self',)
48
49 # depois, fazemos a mesma coisa com a instância na lista de
50 # constantes
51 constants = constants + (inst,)
52
53 # como acrescentamos uma variável, aumentamos o número de
54 # variáveis locais
55 nlocals += 1
56
57 # convertemos a string de código para uma lista com o valor
58 # numérico de cada byte
59 bcode = map(ord, code.co_code)
60
61 # então colocamos no início do código as instruções para fazer
62 # a atribuição self=inst
63 bcode = [LOAD_CONST,
64 len(constants)-1, # carrega a instância na pilha
65 0,
66 STORE_FAST,
67 len(varnames)-1, # atribui a instância a 'self'
68 0] + bcode
69
70
71 # agora vem a parte complicada... infelizmente, quando definimos
72 # as chamadas a 'self' no código sem estar definido localmente, o
73 # interpretador vai buscá-lo como global, e não vai encontrar o
74 # que definimos localmente logo agora... para consertar isso,
75 # precisamos encontrar todas os acessos a globais e substituir
76 # aqueles que tentam acessar o 'self' como global por um acesso
77 # local
78
79 # para facilitar, vamos usar um iterador...
80 itercode = iter(enumerate(bcode))
81
82 while 1:
83 try:
84 i, op = itercode.next()
85 # se a instrução precisa de argumentos, pode nos
86 # interessar ou não, mas de qualquer maneira precisamos
87 # pular os bytes dos argumentos para não confundir um
88 # valor com instrução...
89 if op >= opcode.HAVE_ARGUMENT:
90 # os argumentos vem nos dois bytes seguintes...
91 oparg = itercode.next()[1] + itercode.next()[1]*256
92 # procuramos por LOAD_GLOBAL
93 if op == LOAD_GLOBAL:
94 # o argumento para LOAD_GLOBAL é o índice do nome
95 # da variável em 'names'... se é 'self',
96 # então é o que procuramos!
97 if names[oparg] == 'self':
98 # substituímos a instrução LOAD_GLOBAL por LOAD_FAST
99 bcode[i] = LOAD_FAST
100 # substituímos o argumento para o índice do
101 # nome 'self' na lista 'varnames'
102 bcode[i+1] = len(varnames)-1
103 # a menos que a função tenha mais de 255
104 # variáveis, não teremos problemas aqui e
105 # podemos colocar 0
106 bcode[i+2] = 0
107
108 except StopIteration:
109 # saia do loop quando o iterador esgotar...
110 break
111
112 # transformamos a lista com o código numérico novamente em string
113 codestring = ''.join(map(chr, bcode))
114
115 # recriamos o objeto código com os valores e byte-code novo: muda
116 # apenas 'varnames', 'constants', 'nlocals', e o código, claro...
117 ncode = types.CodeType(argcount, nlocals, stacksize, flags, codestring,
118 constants, names, varnames, filename, name,
119 firstlineno, lnotab, freevars, cellvars)
120 # note que como 'self' vem originalmente como global, o nome vem
121 # em 'names', mas não podemos removê-lo para não atrapalhar com os
122 # índices de outras variáveis...
123
124 # finalmente, recriamos e retornamos a função, com tudo de antes,
125 # mas com o código novo, alterado
126 nfunc = types.FunctionType(ncode, func.func_globals, func.func_name,
127 func.func_defaults, func.func_closure)
128
129 return nfunc
130
131
132 # Depois disso tudo, o resto é moleza...
133
134 class NoSelfMethod(object):
135 # um descriptor que implementa (mais ou menos) o mesmo que os
136 # métodos normais (já que a classe MethodType original não permite
137 # herança)....
138 def __init__(self, func, instance, cls):
139 self.im_func = func
140 self.im_self = instance
141 self.im_class = cls
142
143 def __get__(self, obj, cls):
144 return NoSelfMethod(self.im_func, obj, cls)
145
146 def __call__(self, *args, **kwds):
147 # com a diferença de que quando é chamado com uma instância,
148 # ele usa nossa função nofunc() para criar a nova versão
149 # inserindo o 'self' (sim, ele recria a função fazendo aquilo
150 # tudo cada vez que o método é chamado!)
151 if self.im_self is None:
152 return self.im_func(*args, **kwds)
153 else:
154 func = noself(self.im_func, self.im_self)
155 return func(*args, **kwds)
156
157
158 class NoSelfType(type):
159 # E finalmente, uma metaclasse que faz com que suas classes usem o
160 # nosso método especial
161 def __new__(mcls, name, bases, dic):
162 for k, v in dic.items():
163 if callable(v):
164 dic[k] = NoSelfMethod(v, None, None)
165
166 return type.__new__(NoSelfType, name, bases, dic)
167
168
169 def test():
170
171 # Testando ...
172
173 # Uma global só para garantir que elas não foram alteradas...
174 bar = 'bar'
175
176 class C(object):
177 __metaclass__ = NoSelfType
178
179 def __init__(a, b, c=None):
180 print 'Look mom... no self!', self
181 # confirmando que o 'self' apareceu aqui
182 print 'I am', self, 'they called me with', a, b, c
183
184 def m(obj):
185 # confirmando que o 'self' que aparece aqui e o objeto
186 # criado são de fato os mesmos
187 assert obj is self
188 # confirmando que a global está ok
189 assert bar == 'bar'
190
191 def f():
192 # FIXME: infelizmente, funções aninhadas usam a
193 # instrução LOAD_DEREF para encontrar variáveis do
194 # escopo anterior, que aparecem naquela lista
195 # 'cellvars' lá em cima, então esta função f() não vai
196 # encontrar o 'self' pois tenta procurá-lo como
197 # global, o que significa que teremos de fazer outra
198 # alteração aqui também, bem mais complicada... por
199 # via das dúvidas, vamos testar e conferir que dá
200 # erro em todas as implementações e versões...
201 try:
202 assert obj is self
203 raise AssertionError('self found from nested function?')
204 except NameError:
205 pass
206 f()
207
208 # É isso aí...
209
210 o = C(1, 2, c='foo')
211 o.m(o)
212
213
214 if __name__ == '__main__':
215 test()
Outras implementações
* http://metapython.blogspot.com/2010/11/selfless-python.html