No Python 2.5, existe uma maneira de criar um decorador que decora uma classe? Especificamente, quero usar um decorador para adicionar um membro a uma classe e alterar o construtor para obter um valor para esse membro.
Procurando algo como o seguinte (que possui um erro de sintaxe na 'classe Foo:':
def getId(self): return self.__id
class addID(original_class):
def __init__(self, id, *args, **kws):
self.__id = id
self.getId = getId
original_class.__init__(self, *args, **kws)
@addID
class Foo:
def __init__(self, value1):
self.value1 = value1
if __name__ == '__main__':
foo1 = Foo(5,1)
print foo1.value1, foo1.getId()
foo2 = Foo(15,2)
print foo2.value1, foo2.getId()
Eu acho que o que realmente estou procurando é uma maneira de fazer algo como uma interface C # em Python. Eu preciso mudar meu paradigma, suponho.
fonte
Além da questão de saber se os decoradores da turma são a solução certa para o seu problema:
No Python 2.6 e superior, existem decoradores de classe com o @ -syntax, para que você possa escrever:
Nas versões mais antigas, você pode fazer de outra maneira:
Observe, no entanto, que isso funciona da mesma forma que para os decoradores de funções e que o decorador deve retornar a nova classe (ou original modificado), que não é o que você está fazendo no exemplo. O decorador addID ficaria assim:
Você pode usar a sintaxe apropriada para sua versão do Python, conforme descrito acima.
Mas concordo com os outros que a herança é mais adequada se você deseja substituir
__init__
.fonte
Ninguém explicou que você pode definir dinamicamente classes. Assim, você pode ter um decorador que define (e retorna) uma subclasse:
Que pode ser usado no Python 2 (o comentário de Blckknght, que explica por que você deve continuar fazendo isso no 2.6+) assim:
E no Python 3 como este (mas tenha cuidado para usar
super()
em suas classes):Então você pode ter seu bolo e comê-lo - herança e decoradores!
fonte
super
chamadas na classe original. SeFoo
tivesse um método nomeadofoo
que o chamou,super(Foo, self).foo()
ele recorreria infinitamente, porque o nomeFoo
está vinculado à subclasse retornada pelo decorador, não à classe original (que não é acessível por nenhum nome). O Python 3 sem argumentossuper()
evita esse problema (suponho que através da mesma mágica de compilador que permite que ele funcione). Você também pode solucionar o problema decorando manualmente a classe com um nome diferente (como no exemplo do Python 2.5).Essa não é uma boa prática e não há mecanismo para fazer isso por causa disso. O caminho certo para realizar o que você quer é herança.
Dê uma olhada na documentação da classe .
Um pequeno exemplo:
Dessa forma, Boss tem tudo o que
Employee
tem, com também seu próprio__init__
método e membros.fonte
Concordo que a herança se encaixa melhor no problema apresentado.
Achei essa pergunta realmente útil nas aulas de decoração, obrigado a todos.
Aqui estão outros exemplos, com base em outras respostas, incluindo como a herança afeta as coisas no Python 2.7 (e @wraps , que mantém a documentação original da função, etc.):
Muitas vezes você deseja adicionar parâmetros ao seu decorador:
Saídas:
Eu usei um decorador de funções, pois os acho mais concisos. Aqui está uma aula para decorar uma aula:
Uma versão mais robusta que verifica esses parênteses e funciona se os métodos não existirem na classe decorada:
As
assert
verificações de que o decorador não foi usado sem parênteses. Se tiver, a classe que está sendo decorada é passada para omsg
parâmetro do decorador, que gera umAssertionError
.@decorate_if
aplica somente odecorator
if écondition
avaliado comoTrue
.The
getattr
,callable
test e@decorate_if
são usados para que o decorador não quebre se ofoo()
método não existir na classe que está sendo decorada.fonte
Na verdade, há uma implementação muito boa de um decorador de classe aqui:
https://github.com/agiliq/Django-parsley/blob/master/parsley/decorators.py
Na verdade, acho que essa é uma implementação bastante interessante. Como subclasses da classe que decora, ele se comportará exatamente como essa classe em coisas como
isinstance
cheques.Ele tem um benefício adicional: não é incomum que a
__init__
declaração em um formulário django personalizado faça modificações ou acréscimos,self.fields
por isso é melhor que as alteraçõesself.fields
ocorram após todos os__init__
tiver sido executado para a classe em questão.Muito esperto.
No entanto, em sua classe, você realmente deseja que a decoração altere o construtor, o que não acho que seja um bom caso de uso para um decorador de classe.
fonte
Aqui está um exemplo que responde à pergunta de retornar os parâmetros de uma classe. Além disso, ainda respeita a cadeia de herança, ou seja, apenas os parâmetros da própria classe são retornados. A função
get_params
é adicionada como um exemplo simples, mas outras funcionalidades podem ser adicionadas graças ao módulo de inspeção.fonte
O Django possui
method_decorator
um decorador que transforma qualquer decorador em um decorador de métodos, você pode ver como ele é implementado emdjango.utils.decorators
:https://github.com/django/django/blob/50cf183d219face91822c75fa0a15fe2fe3cb32d/django/utils/decorators.py#L53
https://docs.djangoproject.com/en/3.0/topics/class-based-views/intro/#decorating-the-class
fonte