Pergunta

Eu estou a ensinar-me o Python e o meu mais recente lição foi a de que Python não é Java, e então eu só passei um tempo transformando todos os meus métodos de Classe para as funções.

Agora percebo que eu não preciso usar métodos de Classe para o que eu teria feito com static métodos em Java, mas agora não tenho certeza se eu iria usá-los.Todos os conselhos que eu possa encontrar sobre Python Classe de métodos é ao longo das linhas de novatos como eu, deve afastar-se deles, e o padrão de documentação é, em sua maioria opaco quando discuti-los.

Alguém tem um bom exemplo da utilização de um método de Classe em Python ou, pelo menos, alguém pode me dizer quando os métodos da Classe pode ser usado de forma sensata?

Foi útil?

Solução

Métodos de classe são para quando você precisa ter métodos que não são específicas para um determinado instância, mas ainda envolver a classe de alguma forma.A coisa mais interessante sobre eles é que eles podem ser substituídos por subclasses, algo que simplesmente não é possível em Java métodos estáticos ou Python módulo de funções de nível.

Se você tem uma classe MyClass, e um de nível de módulo de função que opera em MyClass (fábrica de injeção de dependência de stub, etc), torná-lo um classmethod.Em seguida, ele vai estar disponível para subclasses.

Outras dicas

Fábrica de métodos alternativos de construtores) são, de facto, um exemplo clássico de métodos de classe.

Basicamente, os métodos de classe são adequados a qualquer momento que você gostaria de ter um método que, naturalmente, se encaixa no espaço de nomes da classe, mas não está associado a uma instância específica da classe.

Como um exemplo, no excelente unipath módulo:

Diretório atual

  • Path.cwd()
    • Retorno real do diretório atual;por exemplo, Path("/tmp/my_temp_dir").Este é um método de classe.
  • .chdir()
    • Fazer auto diretório atual.

Como o diretório atual é o processo de largura, o cwd o método não tem nenhuma instância específica com a qual ele deve ser associado.No entanto, alterar o cwd para o diretório de um determinado Path instância, de facto, deve ser um método de instância.

Hmmm...como Path.cwd() de fato, um retorno Path exemplo, eu acho que poderia ser considerado um método de fábrica...

Pense da seguinte forma:normal métodos são úteis para esconder os detalhes do envio:você pode digitar myobj.foo() sem se preocupar se o foo() o método é implementado pelo myobj classe do objeto ou de uma de suas principais classes.Métodos de classe são exactamente igual a esta, mas com o objeto de classe em vez disso:eles permitem que você chamar MyClass.foo() sem ter de se preocupar se foo() é implementado especialmente por MyClass porque é necessária a sua própria versão especializada, ou se está permitindo que sua classe pai identificador de chamada.

Métodos de classe são essenciais quando você está fazendo de set-up ou por cálculo, que precede a criação de uma instância real, porque até que a instância existe, obviamente, você não pode usar a instância como o envio para o ponto de chamadas de método.Um bom exemplo pode ser visto na SQLAlchemy código-fonte;dê uma olhada no dbapi() método de classe no seguinte link:

https://github.com/zzzeek/sqlalchemy/blob/ab6946769742602e40fb9ed9dde5f642885d1906/lib/sqlalchemy/dialects/mssql/pymssql.py#L47

Você pode ver que o dbapi() o método, que um backend de banco de dados usa para importar as específicas do fornecedor do banco de dados biblioteca necessidades de ti on-demand, é um método de classe, porque ele precisa executar antes de instâncias de uma determinada conexão de banco de dados começar a criada — mas ela não pode ser uma simples função ou função estática, porque eles querem que ela seja capaz de chamar outros, apoiando métodos que podem da mesma forma deve ser escrita, mais especificamente, no subclasses do que na sua classe pai.E se você expedição para uma função ou classe estática, em seguida, você "esquece" e perder o conhecimento sobre a classe está fazendo a inicialização.

Recentemente, eu queria muito peso-leve o log de classe que teria saída de quantidades variáveis de saída, dependendo do nível de log que pode ser definido por meio de programação.Mas eu não queria instanciar a classe toda vez que eu queria para a saída de uma mensagem de depuração ou de erro ou de aviso.Mas eu também queria para encapsular o funcionamento desta facilidade de registro e torná-lo reutilizável sem a declaração de qualquer globals.

Então eu usei variáveis de classe e o @classmethod decorador para conseguir isso.

Com a minha simples de Registo de aula, eu poderia fazer o seguinte:

Logger._level = Logger.DEBUG

Então, no meu código, se eu queria cuspir um monte de informações de depuração, eu simplesmente tinha de código

Logger.debug( "this is some annoying message I only want to see while debugging" )

Erros poderia ser posto para fora com

Logger.error( "Wow, something really awful happened." )

Na "produção" do ambiente, que pode especificar

Logger._level = Logger.ERROR

e agora, apenas a mensagem de erro será ouvido.A mensagem de depuração não será impresso.

Aqui está a minha classe:

class Logger :
    ''' Handles logging of debugging and error messages. '''

    DEBUG = 5
    INFO  = 4
    WARN  = 3
    ERROR = 2
    FATAL = 1
    _level = DEBUG

    def __init__( self ) :
        Logger._level = Logger.DEBUG

    @classmethod
    def isLevel( cls, level ) :
        return cls._level >= level

    @classmethod
    def debug( cls, message ) :
        if cls.isLevel( Logger.DEBUG ) :
            print "DEBUG:  " + message

    @classmethod
    def info( cls, message ) :
        if cls.isLevel( Logger.INFO ) :
            print "INFO :  " + message

    @classmethod
    def warn( cls, message ) :
        if cls.isLevel( Logger.WARN ) :
            print "WARN :  " + message

    @classmethod
    def error( cls, message ) :
        if cls.isLevel( Logger.ERROR ) :
            print "ERROR:  " + message

    @classmethod
    def fatal( cls, message ) :
        if cls.isLevel( Logger.FATAL ) :
            print "FATAL:  " + message

E algum código que testa-lo um pouco:

def logAll() :
    Logger.debug( "This is a Debug message." )
    Logger.info ( "This is a Info  message." )
    Logger.warn ( "This is a Warn  message." )
    Logger.error( "This is a Error message." )
    Logger.fatal( "This is a Fatal message." )

if __name__ == '__main__' :

    print "Should see all DEBUG and higher"
    Logger._level = Logger.DEBUG
    logAll()

    print "Should see all ERROR and higher"
    Logger._level = Logger.ERROR
    logAll()

Alternativa construtores são o exemplo clássico.

Eu acho que a mais clara resposta é AmanKow do um.Tudo se resume a como u organizar o seu código.Você pode escrever tudo como módulo de funções de nível, que são enrolados no espaço de nomes do módulo i.e

module.py (file 1)
---------
def f1() : pass
def f2() : pass
def f3() : pass


usage.py (file 2)
--------
from module import *
f1()
f2()
f3()
def f4():pass 
def f5():pass

usage1.py (file 3)
-------------------
from usage import f4,f5
f4()
f5()

Acima, o código do procedimento não está bem organizado, como você pode ver, depois de apenas 3 módulos que ele fica confuso, o que é cada método de fazer ?Você pode usar nomes longos descritivos de funções(como em java), mas ainda assim seu código fica incontrolável muito rápido.

A orientação a objetos maneira é dividir o código em blocos gerenciáveis eu.e Classes e objetos e funções podem ser associadas a objetos instâncias ou com classes.

Com funções de classe, você ganha outro nível de divisão no seu código em comparação com o módulo de funções de nível.Assim, você pode relacionados com o grupo de funções dentro de uma classe para torná-las mais específicas para uma tarefa que você atribuiu a essa classe.Por exemplo, você pode criar um arquivo de classe de utilitário :

class FileUtil ():
  def copy(source,dest):pass
  def move(source,dest):pass
  def copyDir(source,dest):pass
  def moveDir(source,dest):pass

//usage
FileUtil.copy("1.txt","2.txt")
FileUtil.moveDir("dir1","dir2")

Desta forma, é mais flexível e mais fácil de manter, você grupo de funções e a sua mais óbvia para o que cada função faz.Você também evitar conflitos de nome, por exemplo, a função de cópia pode existir em outro módulo importado(por exemplo, cópia de rede) que você usar em seu código, assim, quando você usar o nome completo FileUtil.(cópia) você remover o problema e ambas as funções de cópia podem ser usados lado a lado.

Quando um usuário faz logon no meu site, o Usuário() o objeto é instanciado a partir do nome de usuário e senha.

Se eu precisar de um objeto de usuário sem que o usuário seja lá para log in (e.g.um usuário administrador pode excluir outra conta de usuários, então eu preciso instanciar esse usuário e chamar o método delete):

Eu tenho métodos de classe para agarrar o objeto de usuário.

class User():
    #lots of code
    #...
    # more code

    @classmethod
    def get_by_username(cls, username):
        return cls.query(cls.username == username).get()

    @classmethod
    def get_by_auth_id(cls, auth_id):
        return cls.query(cls.auth_id == auth_id).get()

Sinceramente?Eu nunca encontrei um uso para staticmethod ou classmethod.Eu ainda estou para ver uma operação que não pode ser feito usando uma função global ou de um método de instância.

Seria diferente se o python utilizado privado e protegido membros mais como Java.Em Java, eu preciso de um método estático para ser capaz de acessar uma instância privada de membros para fazer as coisas.Em Python, que é raramente necessário.

Normalmente, eu vejo as pessoas usando staticmethods e classmethods quando o que realmente precisamos fazer é usar python módulo de nível de namespaces melhor.

Ele permite que você escreva classe genérica de métodos que você pode usar com qualquer compatíveis classe.

Por exemplo:

@classmethod
def get_name(cls):
    print cls.name

class C:
    name = "tester"

C.get_name = get_name

#call it:
C.get_name()

Se você não usar @classmethod você pode fazê-lo com a palavra-chave self, mas ele precisa de uma instância de Classe:

def get_name(self):
    print self.name

class C:
    name = "tester"

C.get_name = get_name

#call it:
C().get_name() #<-note the its an instance of class C

Comecei a trabalhar com PHP e, recentemente, eu estava me perguntando, o que está acontecendo com este classmethod?Python manual é muito técnico e muito curto, em palavras, por isso não vai ajudar com o entendimento de que o recurso.Eu estava pesquisando e pesquisando e achei resposta -> http://code.anjanesh.net/2007/12/python-classmethods.html.

Se você é preguiçoso, clique nele.A minha explicação é mais curto e abaixo.:)

em PHP (talvez não todos vocês sabem, PHP, mas esta linguagem é tão direto que todo mundo deve entender do que estou falando) temos variáveis estáticas como este:


class A
{

    static protected $inner_var = null;

    static public function echoInnerVar()
    {
        echo self::$inner_var."\n";
    }

    static public function setInnerVar($v)
    {
        self::$inner_var = $v;
    }

}

class B extends A
{
}

A::setInnerVar(10);
B::setInnerVar(20);

A::echoInnerVar();
B::echoInnerVar();

A saída será em ambos os casos, 20.

No entanto, no python podemos adicionar @classmethod decorador e, assim, é possível ter saída de 10 e 20, respectivamente.Exemplo:


class A(object):
    inner_var = 0

    @classmethod
    def setInnerVar(cls, value):
        cls.inner_var = value

    @classmethod
    def echoInnerVar(cls):
        print cls.inner_var


class B(A):
    pass


A.setInnerVar(10)
B.setInnerVar(20)

A.echoInnerVar()
B.echoInnerVar()

Inteligente, não?

Métodos de classe fornecer uma "semântica de açúcar" (não sei se este termo é amplamente usado) ou "semântica conveniência".

Exemplo:você tem um conjunto de classes que representam objetos.Você pode querer ter o método de classe all() ou find() para escrever User.all() ou User.find(firstname='Guido').O que poderia ser feito usando o módulo de funções de nível do curso...

O que só me atingiu, vindo do Ruby, é que um chamado classe método e um chamado instância o método é apenas uma função com significado semântico aplicado ao seu primeiro parâmetro, que é silenciosamente passado quando o função é chamado como um método de uma objeto (i.é. obj.meth()).

Normalmente que objeto deve ser uma instância, mas o @classmethod método de decorador altera as regras de passagem de uma classe.Você pode chamar um método de classe em uma instância (é apenas uma função) - o primeiro argyment será a sua classe.

Porque é apenas uma função, ele só pode ser declarada uma vez que, em qualquer âmbito (por exemplo, class definição).Se segue, portanto, como uma surpresa para um Rubyist, que você não pode ter um método de classe e um método de instância com o mesmo nome.

Considere isto:

class Foo():
  def foo(x):
    print(x)

Você pode chamar foo em uma instância

Foo().foo()
<__main__.Foo instance at 0x7f4dd3e3bc20>

Mas não em uma classe:

Foo.foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method foo() must be called with Foo instance as first argument (got nothing instead)

Agora adicione @classmethod:

class Foo():
  @classmethod
  def foo(x):
    print(x)

Chamada em uma instância, passa agora a sua classe:

Foo().foo()
__main__.Foo

como chamar uma classe:

Foo.foo()
__main__.Foo

É apenas convenção que determina que usamos self para que o primeiro argumento em um método de instância e cls em um método de classe.Eu usei nem aqui, para ilustrar que é apenas um argumento.Em Ruby, self é um palavra-chave.

Contraste com Ruby:

class Foo
  def foo()
    puts "instance method #{self}"
  end
  def self.foo()
    puts "class method #{self}"
  end
end

Foo.foo()
class method Foo

Foo.new.foo()
instance method #<Foo:0x000000020fe018>

O Python método de classe é apenas uma função decorados e você pode usar as mesmas técnicas para crie o seu próprio decoradores.Um decorados método envolve o verdadeiro método (no caso de @classmethod ele passa o adicional argumento de classe).Subjacente método ainda está lá, escondido mas ainda acessível.


nota de rodapé:Eu escrevi depois de um nome de confronto entre uma classe e método de instância despertou a minha curiosidade.Eu estou longe de ser um Python especialista e gostaria de comentários, se alguma de que isso é errado.

Este é um tema interessante.A minha opinião sobre isso é que o python classmethod opera como um singleton, ao invés de uma fábrica (que retorna um produzida uma instância de uma classe).A razão é um singleton é que não há um objeto comum que é produzido (o dicionário), mas apenas uma vez para a classe, mas sim compartilhado por todas as instâncias.

Para ilustrar isso aqui é um exemplo.Note que todos os casos têm uma referência para o único dicionário.Este não é o padrão de Fábrica como eu a entendo.Este é, provavelmente, muito original para python.

class M():
 @classmethod
 def m(cls, arg):
     print "arg was",  getattr(cls, "arg" , None),
     cls.arg = arg
     print "arg is" , cls.arg

 M.m(1)   # prints arg was None arg is 1
 M.m(2)   # prints arg was 1 arg is 2
 m1 = M()
 m2 = M() 
 m1.m(3)  # prints arg was 2 arg is 3  
 m2.m(4)  # prints arg was 3 arg is 4 << this breaks the factory pattern theory.
 M.m(5)   # prints arg was 4 arg is 5

Eu estava me perguntando a mesma pergunta várias vezes.E mesmo que o pessoal aqui se esforçou para explicar isso, IMHO a melhor resposta (e mais simples) resposta que eu tenho encontrado é o descrição o método de Classe na Documentação Python.

Há também referência para o método Estático.E no caso de alguém já conhece métodos de instância (que eu suponho), esta resposta pode ser a peça final para colocá-lo todos juntos...

Mais e mais profundo de elaboração sobre esse tópico podem ser encontradas também na documentação:O tipo de padrão de hierarquia (rolar para baixo para Métodos de instância secção)

Uma classe define um conjunto de instâncias, é claro.E os métodos de uma classe de trabalho em cada uma das ocorrências.A classe (métodos e variáveis) de um lugar para pendurar outras informações relacionadas ao conjunto de instâncias de todos.

Por exemplo, se sua classe define um conjunto de alunos você pode querer variáveis de classe ou métodos que definem como o conjunto de grau, os alunos podem ser membros.

Você também pode usar os métodos da classe para definir ferramentas para trabalhar em todo o conjunto.Por exemplo Estudante.all_of_em() pode retornar todos os conhecidos alunos.Obviamente, se o seu conjunto de instâncias têm mais estrutura do que um conjunto pode fornecer métodos de classe para saber sobre essa estrutura.Alunos.all_of_em(grau='juniores')

Técnicas como esta tendem a levar para armazenar os membros do conjunto de instâncias em estruturas de dados que estão enraizadas em variáveis de classe.Você precisa tomar cuidado para evitar a frustrante a coleta de lixo em seguida.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top