O objetivo é criar uma classe simulada que se comporte como um conjunto de resultados db.
Por exemplo, se uma consulta ao banco de dados retornar, usando uma expressão dict,, {'ab':100, 'cd':200}
eu gostaria de ver:
>>> dummy.ab
100
No começo, pensei que talvez pudesse fazê-lo desta maneira:
ks = ['ab', 'cd']
vs = [12, 34]
class C(dict):
def __init__(self, ks, vs):
for i, k in enumerate(ks):
self[k] = vs[i]
setattr(self, k, property(lambda x: vs[i], self.fn_readyonly))
def fn_readonly(self, v)
raise "It is ready only"
if __name__ == "__main__":
c = C(ks, vs)
print c.ab
mas c.ab
retorna um objeto de propriedade.
Substituir a setattr
linha por k = property(lambda x: vs[i])
não serve para nada.
Então, qual é o caminho certo para criar uma propriedade de instância em tempo de execução?
PS Conheço uma alternativa apresentada em Como o __getattribute__
método é usado?
python
properties
runtime
monkeypatching
Anthony Kong
fonte
fonte
:
e de__init__
referênciasself.fn_readyonly
.Respostas:
Suponho que deveria expandir essa resposta, agora que sou mais velho, mais sábio e sei o que está acontecendo. Antes tarde do que nunca.
Você pode adicionar uma propriedade a uma classe dinamicamente. Mas esse é o problema: você deve adicioná-lo à classe .
A
property
é na verdade uma implementação simples de uma coisa chamada descritor . É um objeto que fornece tratamento personalizado para um determinado atributo, em uma determinada classe . Como uma maneira deif
separar uma árvore enorme__getattribute__
.Quando eu pedir
foo.b
, no exemplo acima, Python vê que ob
definido na classe implementa o protocolo de descritor -que significa apenas que é um objeto com um__get__
,__set__
ou__delete__
método. O descritor assume a responsabilidade de manipular esse atributo, então o Python chamaFoo.b.__get__(foo, Foo)
, e o valor de retorno é passado de volta para você como o valor do atributo. No caso deproperty
, cada um desses métodos apenas chama afget
,fset
oufdel
você passado para oproperty
construtor.Os descritores são realmente a maneira do Python de expor o encanamento de toda a sua implementação OO. De fato, há outro tipo de descritor ainda mais comum que
property
.O método humilde é apenas outro tipo de descritor. Sua
__get__
aderência à instância de chamada como o primeiro argumento; de fato, faz isso:De qualquer forma, suspeito que seja por isso que os descritores só funcionam em classes: eles são uma formalização do material que alimenta as classes em primeiro lugar. Eles são até a exceção à regra: você pode obviamente atribuir descritores a uma classe, e as próprias classes são instâncias
type
! De fato, tentar lerFoo.b
ainda chamadasproperty.__get__
; é apenas idiomático que os descritores retornem a si mesmos quando acessados como atributos de classe.Eu acho muito legal que praticamente todo o sistema OO do Python possa ser expresso em Python. :)
Ah, e eu escrevi um post no blog sobre descritores há algum tempo, se você estiver interessado.
fonte
setattr (Foo, 'name', property (func))
Então, o que você quer é um dicionário onde você possa soletrar a ['b'] como ab?
Isso é fácil:
fonte
d.items = 1
,d.items
retornos<built-in method items of atdict object at ...>
. Você ainda pode fazerd["items"]
ou usar em__getattribute__
vez de__getattr__
, mas isso evita o uso da maioria dos métodos do dict.Parece que você poderia resolver esse problema de maneira muito mais simples com a
namedtuple
, uma vez que você conhece toda a lista de campos com antecedência.Se você absolutamente precisar escrever seu próprio setter, precisará fazer a metaprogramação no nível da classe;
property()
não funciona em instâncias.fonte
namedtuple
merece um prêmio por torná-lo suave e elegante para ser fiel aos princípios orientados a objetos.property()
? não há nada nas respostas que seja específico para propriedades somente leitura.Você não precisa usar uma propriedade para isso. Basta substituir
__setattr__
para torná-los somente leitura.Tada.
fonte
Digamos que você tenha um objeto ao qual deseja adicionar uma propriedade. Normalmente, quero usar propriedades quando preciso começar a gerenciar o acesso a um atributo no código que possui uso downstream, para que eu possa manter uma API consistente. Agora, normalmente os adicionamos ao código-fonte em que o objeto está definido, mas vamos supor que você não tenha esse acesso, ou você precisará realmente escolher dinamicamente suas funções programaticamente.
Crie uma turma
Usando um exemplo baseado na documentação para
property
, vamos criar uma classe de objeto com um atributo "oculto" e criar uma instância dele:Em Python, esperamos que haja uma maneira óbvia de fazer as coisas. No entanto, neste caso, vou mostrar duas maneiras: com notação decoradora e sem. Primeiro, sem a notação do decorador. Isso pode ser mais útil para a atribuição dinâmica de getters, setters ou deleters.
Dinâmico (também conhecido como Monkey Patching)
Vamos criar alguns para a nossa classe:
E agora os atribuímos à propriedade. Observe que poderíamos escolher nossas funções programaticamente aqui, respondendo à pergunta dinâmica:
E uso:
Decoradores
Poderíamos fazer o mesmo que fizemos acima com a notação do decorador, mas nesse caso, devemos nomear os métodos com o mesmo nome (e eu recomendo mantê-lo igual ao atributo), para que a atribuição programática não seja tão trivial quanto está usando o método acima:
E atribua o objeto de propriedade com seus setters e deleters provisionados à classe:
E uso:
fonte
Fiz uma pergunta semelhante neste post do Stack Overflow para criar uma fábrica de classes que criou tipos simples. O resultado foi essa resposta que possuía uma versão funcional da fábrica de classes. Aqui está um trecho da resposta:
Você pode usar alguma variação disso para criar valores padrão, que é seu objetivo (também há uma resposta nessa pergunta que lida com isso).
fonte
Não tenho certeza se eu entendo completamente a pergunta, mas você pode modificar as propriedades da instância em tempo de execução com o built-in
__dict__
da sua classe:fonte
self.__dict__[key] = value
Para quem vem dos mecanismos de pesquisa, eis as duas coisas que eu procurava ao falar sobre propriedades dinâmicas :
__dict__
é bom se você deseja colocar propriedades criadas dinamicamente.__getattr__
é bom fazer algo apenas quando o valor é necessário, como consultar um banco de dados. A combinação set / get é boa para simplificar o acesso aos dados armazenados na classe (como no exemplo acima).Se você deseja apenas uma propriedade dinâmica, consulte a função interna property () .
fonte
Você não pode adicionar um novo
property()
a uma instância em tempo de execução, porque propriedades são descritores de dados. Em vez disso, você deve criar dinamicamente uma nova classe ou sobrecarga__getattribute__
para processar descritores de dados nas instâncias.fonte
A melhor maneira de conseguir é definindo
__slots__
. Dessa forma, suas instâncias não podem ter novos atributos.Que imprime
12
Isso dá:
AttributeError: 'C' object has no attribute 'ab'
fonte
Apenas outro exemplo de como obter o efeito desejado
Então agora podemos fazer coisas como:
fonte
Aqui está uma solução que:
Após a definição da classe, basta fazer isso para adicionar uma propriedade a ela dinamicamente:
Aqui está um exemplo completo, testado em Python 3:
fonte
Você pode usar o seguinte código para atualizar atributos de classe usando um objeto de dicionário:
fonte
Isso é um pouco diferente do que o OP queria, mas agitei meu cérebro até encontrar uma solução funcional, então estou colocando aqui para o próximo cara / garota
Eu precisava de uma maneira de especificar setters e getters dinâmicos.
Conheço meus campos com antecedência, por isso vou criar minhas propriedades. NOTA: você não pode fazer isso por instância, essas propriedades existirão na classe !!!
Vamos testar tudo agora ..
Isso é confuso? Sim, desculpe, não pude apresentar exemplos significativos do mundo real. Além disso, isso não é para os mais claros.
fonte
Isso parece funcionar (mas veja abaixo):
Se você precisar de um comportamento mais complexo, fique à vontade para editar sua resposta.
editar
O seguinte provavelmente seria mais eficiente em termos de memória para grandes conjuntos de dados:
fonte
Para responder ao principal impulso de sua pergunta, você deseja um atributo somente leitura de um ditado como uma fonte de dados imutável:
Vou demonstrar como usar a
namedtuple
partir docollections
módulo para fazer exatamente isso:retorna
100
fonte
E a saída é:
fonte
Estendendo a idéia do kjfletch
Resultado:
fonte
Embora muitas respostas sejam dadas, não consegui encontrar uma com a qual eu esteja feliz. Eu descobri minha própria solução, que faz o
property
trabalho para o caso dinâmico. A fonte para responder à pergunta original:fonte
Algo que funciona para mim é o seguinte:
Resultado
fonte
Recentemente, eu tive um problema semelhante, a solução que eu criei usa
__getattr__
e,__setattr__
para as propriedades que eu quero que ele lide, todo o resto é passado para os originais.fonte
Aqui está o exemplo simples para criar objeto de propriedade programaticamente.
fonte
A única maneira de anexar dinamicamente uma propriedade é criar uma nova classe e sua instância com sua nova propriedade.
fonte
Muitas das respostas fornecidas requerem tantas linhas por propriedade, ou seja, / e / ou - o que eu consideraria uma implementação feia ou tediosa por causa da repetitividade necessária para várias propriedades, etc. Eu prefiro manter as coisas em ordem / simplificar até que elas não pode mais ser simplificado ou até que não sirva muito para isso.
Resumindo: em trabalhos concluídos, se eu repetir duas linhas de código, normalmente a converto em uma função auxiliar de linha única, e assim por diante ... Simplifico argumentos matemáticos ou ímpares como (start_x, start_y, end_x, end_y) para (x, y, w, h) ou seja, x, y, x + w, y + h (às vezes requer min / max ou se w / h são negativos e a implementação não gosta disso, vou subtrair de x / y e abs p / h, etc.).
Substituir os getters / setters internos é um bom caminho a percorrer, mas o problema é que você precisa fazer isso para todas as classes ou orientar a classe nessa base ... Isso não funciona para mim, como eu preferiria ser livre para escolher os filhos / pais por herança, nós filhos, etc.
Eu criei uma solução que responde à pergunta sem usar um tipo de dados Dict para fornecer os dados, pois acho que é tedioso para inserir os dados, etc.
Minha solução exige que você adicione 2 linhas extras acima da sua classe para criar uma classe base para a classe na qual você deseja adicionar as propriedades e, em seguida, 1 linha por e você tem a opção de adicionar retornos de chamada para controlar os dados e informá-lo quando os dados forem alterados , restrinja os dados que podem ser definidos com base no valor e / ou tipo de dados e muito mais.
Você também tem a opção de usar _object.x, _object.x = value, _object.GetX (), _object.SetX (value) e eles são tratados de forma equivalente.
Além disso, os valores são os únicos dados não estáticos atribuídos à instância da classe, mas a propriedade real é atribuída à classe, significando o que você não deseja repetir, não precisa ser repetido ... Você pode atribuir um valor padrão para que o getter não precise dele todas as vezes, embora exista uma opção para substituir o valor padrão padrão e outra opção para que o getter retorne o valor bruto armazenado, substituindo os retornos padrão (observação: este método significa que o valor bruto é atribuído apenas quando um valor é atribuído; caso contrário, é Nenhum - quando o valor é Redefinido, ele atribui Nenhum, etc.)
Também existem muitas funções auxiliares - a primeira propriedade adicionada adiciona mais ou menos 2 auxiliares à classe para referenciar os valores da instância ... Eles são ResetAccessors (_key, ..) varargs repetidos (todos podem ser repetidos usando o primeiro argumento args ) e SetAccessors (_key, _value) com a opção de serem adicionados mais à classe principal para auxiliar na eficiência - os planejados são: uma maneira de agrupar os acessadores, portanto, se você tende a redefinir alguns de cada vez, sempre , você pode atribuí-los a um grupo e redefinir o grupo em vez de repetir as teclas nomeadas a cada vez e muito mais.
O valor armazenado da instância / bruto é armazenado na classe , a classe. referencia a classe Accessor que contém vars / valores / funções estáticos para a propriedade _classe. é a própria propriedade que é chamada quando acessada através da classe de instância durante a configuração / obtenção, etc.
O Accessor _class .__ aponta para a classe, mas como é interno, ele precisa ser atribuído à classe, e foi por isso que eu optei por usar __Name = AccessorFunc (...) para atribuí-la, uma única linha por propriedade com muitas opções opcionais. argumentos a serem usados (usando varargs com chave porque são mais fáceis e mais eficientes de identificar e manter) ...
Também crio muitas funções, como mencionado, algumas das quais usam informações da função de acessador para que não precisem ser chamadas (pois é um pouco inconveniente no momento - agora você precisa usar _class. .FunctionName (_class_instance , args) - Eu usei a pilha / rastreamento para capturar a referência da instância para capturar o valor adicionando as funções que executam essa maratona de bits ou adicionando os acessadores ao objeto e usando self (com o nome this para indicar que eles é para a instância e retém o acesso a si próprio, a referência da classe AccessorFunc e outras informações nas definições de função).
Não está bem feito, mas é um apoio fantástico para os pés. Nota: Se você não usar __Name = AccessorFunc (...) para criar as propriedades, não terá acesso à tecla __, mesmo que eu a defina na função init. Se o fizer, não haverá problemas.
Além disso: Observe que Nome e Chave são diferentes ... O nome é 'formal', usado na Criação de Nome da Função, e a chave é para armazenamento e acesso a dados. ie _class.x onde x minúsculo é a chave, o nome seria X maiúsculo para que GetX () seja a função em vez de Getx () que parece um pouco estranha. isso permite que o self.x funcione e pareça apropriado, mas também permite GetX () e pareça apropriado.
Eu tenho uma classe de exemplo configurada com a chave / nome idêntico e diferente para mostrar. várias funções auxiliares criadas para gerar os dados (Nota: nem tudo isso está completo) para que você possa ver o que está acontecendo.
A lista atual de funções usando a tecla: x, nome: X gera como:
Esta não é de forma alguma uma lista abrangente - existem algumas que ainda não foram publicadas no momento da publicação ...
Alguns dos dados que estão sendo produzidos são:
Isto é para uma nova classe criada usando a classe Demo sem nenhum dado atribuído além do nome (para que possa ser gerado), que é _foo, o nome da variável que eu usei ...
E isso é depois de atribuir todas as propriedades _foo (exceto o nome) os seguintes valores na mesma ordem: 'string', 1.0, True, 9, 10, False
Observe que, devido a tipos de dados restritos ou restrições de valor, alguns dados não foram atribuídos - isso ocorre por design. O setter proíbe que tipos de dados ou valores incorretos sejam atribuídos, mesmo que sejam atribuídos como um valor padrão (a menos que você substitua o comportamento de proteção de valor padrão)
O código não foi publicado aqui porque não havia espaço após os exemplos e explicações ... Também porque ele mudará.
Observação: no momento desta postagem, o arquivo está confuso - isso será alterado. Mas, se você executá-lo em Sublime Text e compilá-lo ou em Python, ele compilará e cuspirá uma tonelada de informações - a parte do AccessorDB não está concluída (que será usada para atualizar os auxiliares Print Getters e GetKeyOutput funções, além de serem alteradas para uma função de Instância, provavelmente inseridas em uma única função e renomeadas - procure-a ..)
Próximo: Nem tudo é necessário para que ele seja executado - muitas das coisas comentadas na parte inferior são para mais informações usadas para depuração - pode não estar presente quando você o baixa. Se for, você poderá descomentar e recompilar para obter mais informações.
Estou procurando uma solução alternativa para a necessidade de MyClassBase: pass, MyClass (MyClassBase): ... - se você souber de uma solução - poste-a.
A única coisa necessária na classe são as linhas __ - o str é para depuração e o init - elas podem ser removidas da classe Demo, mas você precisará comentar ou remover algumas das linhas abaixo (_foo / 2/3 ) ..
As classes String, Dict e Util na parte superior fazem parte da minha biblioteca Python - elas não estão completas. Copiei algumas coisas que precisava da biblioteca e criei algumas novas. O código completo será vinculado à biblioteca completa e o incluirá, além de fornecer chamadas atualizadas e remover o código (na verdade, o único código restante será a Classe Demo e as instruções de impressão - o sistema AccessorFunc será movido para a biblioteca). ..
Parte do arquivo:
Essa beleza facilita incrivelmente a criação de novas classes com propriedades adicionadas dinamicamente com AccessorFuncs / callbacks / data-type / value enforcement, etc.
Por enquanto, o link está em (Este link deve refletir as alterações no documento.): Https://www.dropbox.com/s/6gzi44i7dh58v61/dynamic_properties_accessorfuncs_and_more.py?dl=0
Além disso: se você não usar o Sublime Text, recomendo-o no Notepad ++, Atom, Visual Code e outros por causa de implementações de encadeamento apropriadas que o tornam muito, muito mais rápido de usar ... Também estou trabalhando em um código semelhante ao IDE sistema de mapeamento para ele - consulte: https://bitbucket.org/Acecool/acecoolcodemappingsystem/src/master/ (Adicione o repositório no Package Manager primeiro e depois instale o plug-in - quando a versão 1.0.0 estiver pronta, adicionarei para a lista principal de plugins ...)
Espero que esta solução ajude ... e, como sempre:
Só porque funciona, não dá certo - Josh 'Acecool' Moser
fonte