A melhor maneira de invocar métodos em declarações de classe Python?
-
08-07-2019 - |
Pergunta
Say estou declarando um C
classe e algumas das declarações são muito semelhantes. Eu gostaria de usar uma função f
para reduzir repetição de código para estas declarações. É possível apenas declarar e usar f
como de costume:
>>> class C(object):
... def f(num):
... return '<' + str(num) + '>'
... v = f(9)
... w = f(42)
...
>>> C.v
'<9>'
>>> C.w
'<42>'
>>> C.f(4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method f() must be called with C instance as first argument (got int instance instead)
Ops! Eu inadvertidamente expostos f
para o mundo exterior, mas não é preciso ser um argumento self
(e não pode por razões óbvias). Uma possibilidade seria a de del
a função depois que eu usá-lo:
>>> class C(object):
... def f(num):
... return '<' + str(num) + '>'
... v = f(9)
... del f
...
>>> C.v
'<9>'
>>> C.f
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'C' has no attribute 'f'
Mas e se eu quiser usar f
novamente mais tarde, após a declaração? Não vai fazer para apagar a função. Eu poderia fazê-lo "private" (ou seja, prefixo seu nome com __
) e dar-lhe o tratamento @staticmethod
, mas invocando objetos staticmethod
através de canais anormais fica muito funk:
>>> class C(object):
... @staticmethod
... def __f(num):
... return '<' + str(num) + '>'
... v = __f.__get__(1)(9) # argument to __get__ is ignored...
...
>>> C.v
'<9>'
Eu tenho que usar a loucura acima porque os objetos staticmethod
, que são descritores, não são eles próprios que pode ser chamado. Eu preciso para recuperar a função envolto pelo objeto staticmethod
antes que eu possa chamá-lo.
Não tem que ser uma maneira melhor de fazer isso. Como posso limpa declarar uma função em uma classe, usá-lo durante a sua declaração, e também usá-lo depois de dentro da classe? Devo mesmo estar fazendo isso?
Solução
Muito simplesmente, a solução é que f não precisa ser um membro da classe. Estou assumindo que o seu processo de pensamento passou por um filtro de idioma Javaish causando o bloqueio mental. Ele vai um pouco algo como isto:
def f(n):
return '<' + str(num) + '>'
class C(object):
v = f(9)
w = f(42)
Então, quando você quiser usar f novamente, apenas usá-lo
>>> f(4)
'<4>'
Eu acho que a moral do conto é "Em Python, você não Have à força tudo em uma classe".
Outras dicas
Ali A resposta 's, se você realmente quer evitar f no espaço de nomes do módulo (e usando um nome não exportado como _f, ou configuração __all__ não é suficiente), então você poderia conseguir isso criando a classe dentro de um fechamento.
def create_C():
def f(num):
return '<' + str(num) + '>'
class C(object):
v = f(9)
def method_using_f(self, x): return f(x*2)
return C
C=create_C()
del create_C
Desta forma C tem acesso a f dentro de sua definição e métodos, mas nada mais faz (com exceção de introspecção bastante envolvidos
de seus métodos (C.method_using_f.im_func.func_closure
))
Este é provavelmente um exagero para a maioria dos propósitos embora - documentando que f é interno usando o "_" convenção de prefixo nameing deve geralmente ser suficiente.
[Edit] Uma outra opção é manter uma referência ao objeto de função pré-embalados nos métodos que você deseja usá-lo em Por exemplo, definindo-o como um argumento padrão:.
class C(object):
def f(num):
return '<' + str(num) + '>'
v = f(9)
def method_using_f(self, x, f=f): return f(x*2)
del f
(Embora eu acho que a abordagem de encerramento é provavelmente melhor)
Esta é uma possibilidade:
class _C:
# Do most of the function definitions in here
@classmethod
def f(cls):
return 'boo'
class C(_C):
# Do the subsequent decoration in here
v = _C.f()
Eu acredito que você está tentando fazer isso:
class C():
... class F():
... def __call__(self,num):
... return "<"+str(num)+">"
... f=F()
... v=f(9)
>>> C.v
'<9>'
>>> C.f(25)
'<25>'
>>>
Talvez não há melhor ou mais Python solução ...
"declarar uma função em uma classe, usá-lo durante a sua declaração, e também usá-lo depois de dentro da classe"
Sorry. não pode ser feito.
"Não pode ser feito" não parece se dar bem com Python
Uma opção: escrever uma staticmethod
melhor:
class staticfunc(object):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kw):
return self.func(*args, **kw)
def __repr__(self):
return 'staticfunc(%r)' % self.func
Vamos começar desde o início.
"declarar uma função em uma classe, usá-lo durante a sua declaração, e também usá-lo depois de dentro da classe"
Sorry. não pode ser feito. "Em uma classe" contradiz "usados ??durante a declaração".
- Em um meio classe criada como parte da declaração.
- Usado durante meio de declaração ela existe fora da classe. Muitas vezes, como uma classe meta. No entanto, existem outras maneiras.
Não está claro o que C.w e C.v é suposto ser. eles são apenas cordas? Se assim for, um f
função externa é a melhor solução. O "não atravancar a namespace" é um pouco enganoso. Afinal, você quer usá-lo novamente.
É no mesmo módulo como C. É por isso que Python tem módulos. Ele se liga a função e classe juntos.
import myCmod
myCmod.C.w
myCmod.C.v
myCmod.f(42)
Se w e V não são cadeias simples, há uma solução muito boa que dá muita flexibilidade.
Geralmente, para a classe de nível ( "estático") variáveis ??como esta, podemos usar outras classes. Não é possível alcançar completamente a API desejado, mas isso é perto.
>>> class F(object):
def __init__( self, num ):
self.value= num
self.format= "<%d>" % ( num, )
>>> class C(object):
w= F(42)
v= F(9)
>>> C.w
<__main__.F object at 0x00C58C30>
>>> C.w.format
'<42>'
>>> C.v.format
'<9>'
A vantagem disto é que F é uma coisa adequada, de primeira classe que pode ser estendido. Não é uma coisa "escondida" que nós estamos tentando evitar a exposição. É um fato da vida, de modo que poderia muito bem seguir o / princípio de fechamento Abrir e torná-lo abrir a extensão.