Não sei quando o atributo deve ser privado e se devo usar a propriedade.
Eu li recentemente que setters e getters não são pythônicos e eu deveria usar o decorador de propriedade. Está certo.
Mas e se eu tiver um atributo, que não deve ser definido de fora da classe, mas pode ser lido (atributo somente leitura). Este atributo deve ser privado, e por privado quero dizer com sublinhado, assim self._x
? Se sim, como posso ler sem usar getter? O único método que conheço agora é escrever
@property
def x(self):
return self._x
Dessa forma, posso ler o atributo, obj.x
mas não consigo defini-lo, obj.x = 1
então está tudo bem.
Mas devo realmente me preocupar em definir o objeto que não deve ser definido? Talvez eu deva apenas deixar isso. Mas, novamente, não posso usar o sublinhado porque a leitura obj._x
é estranha para o usuário, então devo usar obj.x
e, novamente, o usuário não sabe que não deve definir este atributo.
Qual é a sua opinião e prática?
fonte
self.x
e confiar que ninguém vai mudarx
. Se certificar-se de que issox
não pode ser alterado for importante, use uma propriedade._x
não é nada estranho: por convenção , significa algo "privado".object
, para que isso realmente pare de configurarobj.x
. Em uma classe de estilo antigo, você ainda pode definirobj.x
, com resultados bastante inesperados.Respostas:
Geralmente, os programas Python devem ser escritos com a suposição de que todos os usuários são adultos consentidos e, portanto, são responsáveis por usar as coisas corretamente. No entanto, na rara instância em que simplesmente não faz sentido que um atributo seja configurável (como um valor derivado ou um valor lido de alguma fonte de dados estática), a propriedade somente getter é geralmente o padrão preferido.
fonte
Apenas meus dois centavos, Silas Ray está no caminho certo, no entanto, queria adicionar um exemplo. ;-)
Python é uma linguagem insegura e, portanto, você sempre terá que confiar que os usuários de seu código usarão o código como uma pessoa razoável (sensata).
Por PEP 8 :
Para ter uma propriedade 'somente leitura' em uma classe de que você pode fazer uso da
@property
decoração, você precisará herdar deobject
quando fizer isso para usar as classes de novo estilo.Exemplo:
fonte
self.__a = []
, se , você ainda pode fazer issoa.a.append('anything')
e vai funcionar.Aqui está uma maneira de evitar a suposição de que
por favor veja minha atualização abaixo
Usando
@property
, é muito detalhado, por exemplo:Usando
Exceto o último, é uma convenção. Você ainda pode, se realmente tentar, acessar variáveis com sublinhado duplo.
Então, o que fazemos? Desistimos de ter propriedades somente leitura em Python?
Ver!
read_only_properties
decorador para o resgate!Você pergunta:
Que bom que você perguntou, aqui está a fonte para read_only_properties :
atualizar
Nunca esperei que essa resposta recebesse tanta atenção. Surpreendentemente, sim. Isso me incentivou a criar um pacote que você possa usar.
em seu shell python:
fonte
if..elif..else
bloqueio poderia ser apenasif name in attrs and name in self.__dict__: raise Attr...
sem necessidadepass
. Problema 1: as classes assim decoradas terminam todas com um idêntico__name__
, e a representação em string de seu tipo também é homogeneizada. Problema 2: esta decoração sobrescreve qualquer custom__setattr__
. Problema 3: os usuários podem derrotar isso comdel MyClass.__setattr__
.object.__setattr__(f, 'forbidden', 42)
. Não vejo o queread_only_properties
adiciona que não seja manipulado pela mutilação do nome de sublinhado duplo.Aqui está uma abordagem um pouco diferente para propriedades somente leitura, que talvez devam ser chamadas de propriedades de gravação única, uma vez que precisam ser inicializadas, não é? Para os paranóicos entre nós que se preocupam em poder modificar propriedades acessando o dicionário do objeto diretamente, introduzi a mutilação "extrema" de nomes:
fonte
dict_name
, por exemplodict_name = "_spam_" + name
, remove a dependênciauuid4
e torna a depuração muito mais fácil.p.__dict__['_spam_x'] = 5
para alterar o valor dep.x
, portanto, isso não fornece mutilação de nome suficiente.Estou insatisfeito com as duas respostas anteriores para criar propriedades somente leitura porque a primeira solução permite que o atributo somente leitura seja excluído e, em seguida, definido e não bloqueia o __dict__. A segunda solução pode ser contornada com testes - encontrar o valor igual ao que você definiu como dois e alterá-lo eventualmente.
Agora, para o código.
Não faz sentido criar atributos somente leitura, exceto quando está escrevendo código de biblioteca, código que está sendo distribuído a outros como código para usar a fim de aprimorar seus programas, e não código para qualquer outro propósito, como desenvolvimento de aplicativo. O problema __dict__ foi resolvido, porque o __dict__ agora é dos tipos imutáveis.MappingProxyType , portanto, os atributos não podem ser alterados por meio do __dict__. Definir ou excluir __dict__ também é bloqueado. A única maneira de alterar as propriedades somente leitura é alterando os métodos da própria classe.
Embora eu acredite que minha solução seja melhor do que as duas anteriores, ela poderia ser melhorada. Estes são os pontos fracos deste código:
a) Não permite adicionar a um método em uma subclasse que define ou exclui um atributo somente leitura. Um método definido em uma subclasse é automaticamente impedido de acessar um atributo somente leitura, mesmo chamando a versão da superclasse do método.
b) Os métodos somente leitura da classe podem ser alterados para evitar as restrições de somente leitura.
No entanto, não há como, sem editar a classe, definir ou excluir um atributo somente leitura. Isso não depende de convenções de nomenclatura, o que é bom porque o Python não é tão consistente com as convenções de nomenclatura. Isso fornece uma maneira de criar atributos somente leitura que não podem ser alterados com lacunas ocultas sem editar a própria classe. Simplesmente liste os atributos a serem lidos somente ao chamar o decorador como argumentos e eles se tornarão somente leitura.
Crédito para a resposta de Brice em Como obter o nome da classe do chamador dentro de uma função de outra classe em python? para obter as classes e métodos do chamador.
fonte
object.__setattr__(pfi, 'readonly', 'foobar')
quebra essa solução, sem editar a própria classe.Observe que os métodos de instância também são atributos (da classe) e que você pode defini-los no nível da classe ou da instância se realmente quiser ser durão. Ou que você pode definir uma variável de classe (que também é um atributo da classe), onde as propriedades úteis somente leitura não funcionarão perfeitamente fora da caixa. O que estou tentando dizer é que o problema do "atributo somente leitura" é, na verdade, mais geral do que normalmente é percebido. Felizmente, existem expectativas convencionais em ação que são tão fortes que nos cegam contra esses outros casos (afinal, quase tudo é um atributo de algum tipo em python).
Com base nessas expectativas, acho que a abordagem mais geral e leve é adotar a convenção de que os atributos "públicos" (sem sublinhado inicial) são somente leitura, exceto quando explicitamente documentados como graváveis. Isso inclui a expectativa usual de que os métodos não serão corrigidos e as variáveis de classe que indicam os padrões da instância são melhores, quanto mais. Se você se sentir realmente paranóico com algum atributo especial, use um descritor somente leitura como medida de último recurso.
fonte
Embora eu goste do decorador de classe de Oz123, você também pode fazer o seguinte, que usa um wrapper de classe explícito e __new__ com um método Factory de classe que retorna a classe dentro de um encerramento:
fonte
Essa é a minha solução alternativa.
fonte
alguém mencionou usar um objeto proxy, eu não vi um exemplo disso, então acabei experimentando, [mal].
/! \ Por favor, prefira definições de classe e construtores de classe, se possível
este código está reescrevendo efetivamente
class.__new__
(construtor de classe), exceto pior em todos os sentidos. Evite a dor e não use esse padrão, se puder.fonte
Eu sei que estou trazendo de volta dos mortos este tópico, mas eu estava procurando como tornar uma propriedade somente leitura e depois de encontrar este tópico, não fiquei satisfeito com as soluções já compartilhadas.
Então, voltando à pergunta inicial, se você começar com este código:
E você deseja tornar o X somente leitura, você pode simplesmente adicionar:
Então, se você executar o seguinte:
fonte
AttributeError('can't set attribute')
)