Definição de classe dinâmica em Python

A definição de objeto dinâmico pode ser entendida como definição em tempo de execução. Ao contrário de uma definição estática, que é usada na definição de palavra-chave familiar de uma classe class, uma definição dinâmica usa uma classe embutida type.



Metaclasse de tipo



A classe de tipo geralmente é usada para obter o tipo de um objeto. Por exemplo, assim:



h = "hello"
type(h)
<class 'str'>


Mas tem outros usos. Ele pode inicializar novos tipos.Como você sabe, tudo em Python é um objeto. Conclui-se que todas as definições têm tipos, incluindo classes e objetos. Por exemplo:



class A:
    pass
type(A)
<class 'type'>


Pode não estar totalmente claro por que uma classe é atribuída a um tipo de classe type, ao contrário de suas instâncias:



a = A()
type(a)
<class '__main__.A'>


O objeto aé atribuído a uma classe como um tipo. É assim que o intérprete trata o objeto como uma instância da classe. A própria classe tem um tipo de classe typeporque o herda da classe base object:



A.__bases__
(<class 'object'>,)


Tipo de aula object:



type(object)
<class 'type'>


A classe é objectherdada por todas as classes por padrão, ou seja:



class A(object):
    pass


Igual a:



class A:
    pass


A classe sendo definida herda a classe base como um tipo. No entanto, isso não explica por que a classe base objecté do tipo de classe type. A questão é que typeé uma metaclasse. Como você já sabe, todas as classes herdam da classe base object, que é do tipo metaclasse type. Portanto, todas as classes também têm este tipo, incluindo a própria metaclasse type:



type(type)
<class 'type'>


Este é o "ponto final da digitação" em Python. A cadeia de herança de tipo está fechada na classe type. A metaclasse typeserve como base para todas as classes em Python. É fácil verificar isso:



builtins = [list, dict, tuple]
for obj in builtins:
    type(obj)
<class 'type'>
<class 'type'>
<class 'type'>


Uma classe é um tipo de dado abstrato e as instâncias dela têm uma referência de classe como um tipo.



Inicializando novos tipos com a classe de tipo



Ao verificar os tipos, a classe é typeinicializada com um único argumento:



type(object) -> type


Ao fazer isso, ele retorna o tipo do objeto. No entanto, a classe implementa um método de inicialização diferente com três argumentos, que retorna um novo tipo:



type(name, bases, dict) -> new type


Parâmetros de inicialização de tipo



  • name

    Uma string que define o nome da nova classe (tipo).
  • bases

    Uma tupla de classes básicas (classes que a nova classe herdará).
  • dict

    Dicionário com os atributos da futura classe. Normalmente com strings em chaves e tipos que podem ser chamados em valores.


Definição de classe dinâmica



Inicializamos a classe do novo tipo, fornecendo todos os argumentos necessários e a chamamos de:



MyClass = type("MyClass", (object, ), dict())
MyClass
<class '__main__.MyClass'>


Você pode trabalhar com a nova turma normalmente:



m = MyClass()
m
<__main__.MyClass object at 0x7f8b1d69acc0>


Além disso, o método é equivalente à definição de classe usual:



class MyClass:
    pass


Definição dinâmica de atributos de classe



Há pouco sentido em uma classe vazia, então surge a pergunta: como adicionar atributos e métodos?



Para responder a esta pergunta, considere o código de inicialização inicial:



MyClass = type(“MyClass”, (object, ), dict())


Normalmente, os atributos são adicionados à classe no estágio de inicialização como o terceiro argumento - o dicionário. Você pode especificar nomes e valores de atributos no dicionário. Por exemplo, pode ser uma variável:



MyClass = type(“MyClass”, (object, ), dict(foo=“bar”)
m = MyClass()
m.foo
'bar'


Definição de método dinâmico



Objetos chamáveis ​​também podem ser passados ​​para o dicionário, por exemplo, métodos:



def foo(self):
    return “bar”
MyClass = type(“MyClass”, (object, ), dict(foo=foo))
m = MyClass()
m.foo
'bar'


Este método tem uma desvantagem significativa - a necessidade de definir o método estaticamente (acho que, no contexto de tarefas de metaprogramação, isso pode ser considerado uma desvantagem). Além disso, definir um método com um parâmetro selffora do corpo da classe parece estranho. Então, vamos voltar à inicialização dinâmica de uma classe sem atributos:



MyClass = type(“MyClass”, (object, ), dict())


Depois de inicializar uma classe vazia, você pode adicionar métodos a ela dinamicamente, ou seja, sem uma definição estática explícita:



code = compile('def foo(self): print(“bar”)', "<string>", "exec")


compileÉ uma função interna que compila o código-fonte em um objeto. O código pode ser executado por funções exec()ou eval().



Parâmetros da função de compilação



  • fonte O código- fonte

    pode ser um link para um módulo.
  • filename

    O nome do arquivo no qual o objeto será compilado.
  • modo

    Se especificado "exec", a função compilará o código-fonte em um módulo.


O resultado do trabalho compileé um objeto de classe code:



type(code)
<class 'code'>


O objeto codeprecisa ser convertido em um método. Como um método é uma função, começamos convertendo um codeobjeto de classe em um objeto de classe function. Para fazer isso, importe o módulo types:



from types import FunctionType, MethodType


Vou importá- MethodTypelo porque vou precisar dele mais tarde para converter a função em um método de classe.



function = FunctionType(code.co_consts[0], globals(), “foo”)


Parâmetros do método de inicialização FunctionType



  • code

    Objeto de classe code. code.co_consts[0]É uma chamada para um descritor de co_constsclasse code, que é uma tupla com constantes no código do objeto. Imagine um objeto codecomo um módulo com uma única função que estamos tentando adicionar como um método de classe. 0É seu índice, pois é a única constante do módulo.
  • globals()

    Dicionário de variáveis ​​globais.
  • name

    Parâmetro opcional que especifica o nome da função.


O resultado é uma função:



function
<function foo at 0x7fc79cb5ed90>
type(function)
<class 'function'>


Em seguida, você precisa adicionar esta função como um método de classe MyClass:



MyClass.foo = MethodType(function, MyClass)


Uma expressão bastante simples que atribui nossa função a um método de classe MyClass.



m = MyClass()
m.foo()
bar


Atenção



Em 99% dos casos, você pode sobreviver com definições de classe estáticas. No entanto, o conceito de metaprogramação é bom para revelar os aspectos internos do Python. Muito provavelmente, será difícil para você encontrar a aplicação dos métodos descritos aqui, embora em minha prática tenha existido tal caso.



Você já trabalhou com objetos dinâmicos? Talvez em outras línguas?



Links






All Articles