Como evitar 'self' explícito em Python?

131

Eu tenho aprendido Python seguindo alguns tutoriais de pygame .

Nele, encontrei o uso extensivo da palavra-chave self e, vindo de um background principalmente em Java, acho que continuo esquecendo de digitar self . Por exemplo, em vez de self.rect.centerxdigitar rect.centerx, porque, para mim, rect já é uma variável de membro da classe.

O paralelo Java em que posso pensar para esta situação está tendo que prefixar todas as referências a variáveis-membro com isso .

Estou preso prefixando todas as variáveis ​​de membro com self , ou existe uma maneira de declará-las que me permitiria evitar isso?

Mesmo que o que estou sugerindo não seja pitônico , ainda gostaria de saber se é possível.

Dei uma olhada nessas perguntas relacionadas ao SO, mas elas não respondem muito bem o que estou procurando:

bguiz
fonte
5
Eu venho de um background Java e acho natural, mas adiciono explicitamente "this" a cada chamada para deixar mais claro que estou me referindo a uma variável de instância.
Uri
4
Você conhece a convenção de um m_prefixo para todos os nomes de membros observados por alguns programadores de C ++ / Java? O uso de self.ajuda na legibilidade de maneira semelhante. Além disso, você deve ler dirtsimple.org/2004/12/python-is-not-java.html .
Beni Cherniavsky-Paskin
2
Embora geralmente m_seja usado apenas para membros de dados não públicos não estáticos (pelo menos em C ++).
@Beni ótimo artigo vinculado, e sim, de fato, eu realmente sigo a convenção de usar mVariableName, para variáveis-membro, ao codificar em Java. Acho que o comentário do @ Anurag resume muito bem o que um desenvolvedor Java deve fazer ao aprender python.
bguiz
11
Então, como todos estão dizendo ao OP porque o uso de self é bom / necessário / etc. mas ninguém diz se pode ou não ser evitado de alguma forma? Mesmo que por algum tipo de truque sujo?
einpoklum

Respostas:

99

Python requer especificação própria. O resultado é que nunca há confusão sobre o que é membro e o que não é, mesmo sem a definição completa da classe visível. Isso leva a propriedades úteis, como: você não pode adicionar membros que ocultam acidentalmente os não membros e, portanto, quebram o código.

Um exemplo extremo: você pode escrever uma classe sem ter conhecimento de quais classes base ela pode ter e sempre saber se está acessando um membro ou não:

class A(some_function()):
  def f(self):
    self.member = 42
    self.method()

Esse é o código completo ! (some_function retorna o tipo usado como base.)

Outro, onde os métodos de uma classe são compostos dinamicamente:

class B(object):
  pass

print B()
# <__main__.B object at 0xb7e4082c>

def B_init(self):
  self.answer = 42
def B_str(self):
  return "<The answer is %s.>" % self.answer
# notice these functions require no knowledge of the actual class
# how hard are they to read and realize that "members" are used?

B.__init__ = B_init
B.__str__ = B_str

print B()
# <The answer is 42.>

Lembre-se de que ambos os exemplos são extremos e você não os verá todos os dias, nem estou sugerindo que você escreva frequentemente códigos como esse, mas eles mostram claramente aspectos de auto explicitamente exigidos.


fonte
4
Obrigado. Essa resposta chega ao ponto porque também explica os benefícios do uso self.
bguiz
2
@ Roger Pate: Por favor, pare de editar minha pergunta para remover o python dele. Eu acho que pertence lá. (e obrigado pela resposta!)
bguiz
3
@guguiz: É tão convenção não duplicar as tags no título. No entanto, quando editei 2 dias atrás, não o vi revertido há 7 meses.
1
Seria bom se o eu pudesse ser reduzido a um único personagem.
dwjohnston
5
Na verdade, essa é uma razão bastante boba. O Python pode deixar de aceitar o sombreamento e exigir que você o declare especificamente, ou seja, 'abençoe' seu campo de sombreamento com algum tipo de __shadow__ myFieldName. Isso impediria sombreamento acidental também, não é?
Einpoklum 09/04
39

As respostas anteriores são basicamente variantes de "você não pode" ou "você não deveria". Embora eu concorde com o último sentimento, tecnicamente a pergunta ainda não foi respondida.

Além disso, existem razões legítimas pelas quais alguém pode querer fazer algo como a pergunta real. Uma coisa que eu encontro às vezes são longas equações matemáticas, nas quais o uso de nomes longos torna a equação irreconhecível. Aqui estão algumas maneiras de como você pode fazer isso em um exemplo fixo:

import numpy as np
class MyFunkyGaussian() :
    def __init__(self, A, x0, w, s, y0) :
        self.A = float(A)
        self.x0 = x0
        self.w = w
        self.y0 = y0
        self.s = s

    # The correct way, but subjectively less readable to some (like me) 
    def calc1(self, x) :
        return (self.A/(self.w*np.sqrt(np.pi))/(1+self.s*self.w**2/2)
                * np.exp( -(x-self.x0)**2/self.w**2)
                * (1+self.s*(x-self.x0)**2) + self.y0 )

    # The correct way if you really don't want to use 'self' in the calculations
    def calc2(self, x) :
        # Explicity copy variables
        A, x0, w, y0, s = self.A, self.x0, self.w, self.y0, self.s
        sqrt, exp, pi = np.sqrt, np.exp, np.pi
        return ( A/( w*sqrt(pi) )/(1+s*w**2/2)
                * exp( -(x-x0)**2/w**2 )
                * (1+s*(x-x0)**2) + y0 )

    # Probably a bad idea...
    def calc3(self, x) :
        # Automatically copy every class vairable
        for k in self.__dict__ : exec(k+'= self.'+k)
        sqrt, exp, pi = np.sqrt, np.exp, np.pi
        return ( A/( w*sqrt(pi) )/(1+s*w**2/2)
                * exp( -(x-x0)**2/w**2 )
                * (1+s*(x-x0)**2) + y0 )

g = MyFunkyGaussian(2.0, 1.5, 3.0, 5.0, 0.0)
print(g.calc1(0.5))
print(g.calc2(0.5))
print(g.calc3(0.5))

O terceiro exemplo - ou seja, usar for k in self.__dict__ : exec(k+'= self.'+k)é basicamente o que a pergunta está realmente pedindo, mas deixe-me esclarecer que geralmente não acho que seja uma boa ideia.

Para obter mais informações e maneiras de percorrer variáveis ​​de classe ou até funções, consulte as respostas e a discussão desta pergunta . Para uma discussão de outras maneiras de nomear dinamicamente variáveis ​​e por que isso geralmente não é uma boa ideia, consulte esta postagem no blog .

ATUALIZAÇÃO: Parece não haver maneira de atualizar ou alterar dinamicamente os locais em uma função no Python3, portanto, o calc3 e variantes semelhantes não são mais possíveis. A única solução compatível com python3 que consigo pensar agora é usar globals:

def calc4(self, x) :
        # Automatically copy every class variable in globals
        globals().update(self.__dict__)
        sqrt, exp, pi = np.sqrt, np.exp, np.pi
        return ( A/( w*sqrt(pi) )/(1+s*w**2/2)
                * exp( -(x-x0)**2/w**2 )
                * (1+s*(x-x0)**2) + y0 )

O que, novamente, seria uma prática terrível em geral.

argentum2f
fonte
4
Brilhante! Essa é a resposta mais correta (apenas?). +1 Você também fornece uma razão prática única para fazê-lo. + 1i
David Lotts
Depois de criar uma classe e mover o código para dentro dela: agora todos os métodos e variáveis ​​não são mais reconhecidos (não eu ...). Me lembro de mais uma razão pela qual o python e não me dou bem. Obrigado por isso como uma idéia. Ele não corrige o problema / dor de cabeça / ilegibilidade geral, mas fornece uma solução alternativa modesta.
javadba
Por que não apenas atualizar em localsvez de usar exec?
Nathan
Eu tentei locals().update(self.__dict__)em python 2 e 3, mas não funcionou. No python3, mesmo o truque 'exec' não é mais uma opção. Por outro lado, globals().update(self.__dict__)funciona, mas seria uma prática terrível em geral.
argentum2f 17/03
26

Na verdade, selfnão é uma palavra-chave, é apenas o nome dado convencionalmente ao primeiro parâmetro dos métodos de instância no Python. E esse primeiro parâmetro não pode ser ignorado, pois é o único mecanismo que um método tem para saber em qual instância da sua classe está sendo chamada.

Michał Marczyk
fonte
1
Essa resposta, especialmente a segunda frase, é muito muito mais útil do que a resposta aceite para mim, porque eu posso saber 'auto explícita' é apenas uma das limitações em Python e não evitável
QuestionDriven
21

Você pode usar o nome que quiser, por exemplo

class test(object):
    def function(this, variable):
        this.variable = variable

ou mesmo

class test(object):
    def function(s, variable):
        s.variable = variable

mas você está preso ao usar um nome para o escopo.

Eu não recomendo que você use algo diferente de si mesmo, a menos que tenha um motivo convincente, pois isso o tornaria estranho para os pythonistas experientes.

Esteban Küber
fonte
26
Você pode fazer isso, mas não ! Não há razão para tornar seu código mais estranho do que precisa. Sim, você pode chamá-lo de qualquer coisa, mas a convenção é chamá-lo selfe você deve seguir a convenção. Isso tornará seu código mais fácil de entender para qualquer programador Python experiente que o veja. (Isso inclui você, daqui a seis meses, tentando descobrir o que o seu antigo programa faz!)
steveha
3
Ainda mais estranho:def function(_, variable): _.variable = variable
Bob Stein
1
@ BobStein-VisiBone ainda mais alienígena:def funcion(*args): args[0].variable = args[1]
Aemyl
4
@ steveha ele não está recomendando, esta informação é muito útil para pessoas como eu que não sabiam que você pode usar palavras-chave diferentes das próprias e se perguntam por que um objeto da própria classe é passado.
Sven van den Boogaart #
> "mais estranho". O Python é estranho - especialmente as estruturas de classes e esse uso do self é um canário para apoiar a legibilidade. Eu sei que muitos defensores virão depois disso, mas isso não muda a veracidade.
javadba
9

sim, você sempre deve especificar self, porque explícito é melhor que implícito, de acordo com a filosofia python.

Você também descobrirá que a maneira como você programa em python é muito diferente da maneira como você programa em java; portanto, o uso de selftende a diminuir porque você não projeta tudo dentro do objeto. Em vez disso, você faz um uso maior da função no nível do módulo, que pode ser melhor testada.

a propósito. Eu odiava no começo, agora eu odeio o oposto. o mesmo para controle de fluxo acionado por recuo.

Stefano Borini
fonte
2
"Você faz um uso maior da função no nível do módulo, que pode ser melhor testada" é duvidosa, e eu tenho que discordar totalmente. É verdade que você não é forçado a transformar tudo em um método (estático ou não) de alguma classe, independentemente de ser "em nível de módulo logicamente", mas isso não tem nada a ver com si mesmo e não tem impacto significativo sobre testando de um jeito ou de outro (para mim).
Surge da minha experiência. Eu não o sigo como um mantra todas as vezes, mas torna as coisas mais fáceis de testar se você colocar algo que não exija acesso à variável de membro como um método separado e independente. sim, você separa a lógica dos dados, o que sim, é contra o OOP, mas eles estão juntos, apenas no nível do módulo. Não dou a uma ou a outra a melhor nota, é apenas uma questão de gosto. Às vezes, eu me pego especificando métodos de classe que não têm nada a ver com a própria classe, pois eles não tocam selfem nada. Então, qual é o sentido de tê-los na aula?
Stefano Borini
Eu discordo de ambas as partes (parece que não uso "métodos não" com mais frequência do que em outras línguas), mas "o que pode ser melhor testado" implica que uma maneira é superior à outra (é assim que eu leia-o? não parece provável), embora não encontre suporte para isso na minha experiência. Observe que não estou dizendo que você deve sempre usar um ou outro, mas apenas métodos e não métodos são igualmente capazes de serem testados.
5
sim, é claro que você pode, mas a abordagem é diferente. Um objeto tem um estado, um método no nível do módulo não. Se você descobrir que um teste falha ao testar um método em nível de classe, duas coisas podem estar erradas: 1) o estado do objeto no momento da chamada 2) o próprio método. Se você tiver um método no nível de módulo sem estado, apenas o caso 2 poderá acontecer. Você moveu a configuração do objeto (onde está a caixa preta no que diz respeito ao teste, pois é governada pela lógica eventualmente complexa dentro do objeto) para o testsuite. Você está reduzindo a complexidade e mantendo um controle mais rígido da instalação.
Stefano Borini
1
"Se você possui um método no nível de módulo sem estado", que tal um método no nível de módulo com estado? Tudo o que você está me dizendo é que as funções sem estado são mais fáceis de testar do que as com estado, e eu concordo com isso, mas não tem nada a ver com métodos versus não métodos. Veja o parâmetro próprio exatamente como isso: apenas outro parâmetro para a função.
4

O "self" é o espaço reservado convencional da instância atual do objeto de uma classe. É usado quando você deseja se referir à propriedade, ao campo ou ao método do objeto dentro de uma classe, como se estivesse se referindo a "ele próprio". Mas, para tornar mais curto, alguém no domínio de programação Python começou a usar "self", outros domínios usam "this", mas o fazem como uma palavra-chave que não pode ser substituída. Eu usei "its" para aumentar a legibilidade do código. É uma das coisas boas em Python - você tem a liberdade de escolher seu próprio espaço reservado para a instância do objeto que não seja "self". Exemplo para self:

class UserAccount():    
    def __init__(self, user_type, username, password):
        self.user_type = user_type
        self.username = username            
        self.password = encrypt(password)        

    def get_password(self):
        return decrypt(self.password)

    def set_password(self, password):
        self.password = encrypt(password)

Agora substituímos 'self' por 'its':

class UserAccount():    
    def __init__(its, user_type, username, password):
        its.user_type = user_type
        its.username = username            
        its.password = encrypt(password)        

    def get_password(its):
        return decrypt(its.password)

    def set_password(its, password):
        its.password = encrypt(password)

qual é mais legível agora?

LEMUEL ADANE
fonte
porque não basta s(ou alguma outra letra única) em vez deits
javadba
'its' tem um significado e 's' não.
LEMUEL ADANE
stem o mesmo significado: um alias para a instância da classe. eu tenho que olhar para cima o que itssignifica no contexto do mesmo jeito
javadba
Ambos não são legíveis para mim. Ainda é ilógico ter "você mesmo" como um parâmetro externo, não faz nenhum sentido.
Cesar
3

self faz parte da sintaxe python para acessar membros de objetos, então, receio que você esteja preso a ela

Charles Ma
fonte
2
self é uma maneira de dizer ao modificador de acesso sem realmente usá-lo. +1
Perpetualcoder
1

Na verdade, você pode usar a receita "Eu implícito" da apresentação de Armin Ronacher "5 anos de más idéias" (pesquise no google).

É uma receita muito inteligente, como quase tudo, de Armin Ronacher, mas não acho que essa ideia seja muito atraente. Eu acho que prefiro isso explícito em C # / Java.

Atualizar. Link para "receita para más ideias": https://speakerdeck.com/mitsuhiko/5-years-of-bad-ideas?slide=58

Alex Yu
fonte
É este o link ao qual você está se referindo? Se assim pls incluir em sua resposta
reubenjohn
Não, a "má ideia" de Armin parece mais engraçada para o meu gosto. Eu incluí link.
Alex Yu
Antes de clicar no link da receita, observe que isso o torna implícito na lista de parâmetros def method(<del> self </del> ), mas self.variableainda é necessário com este truque inteligente.
precisa saber é o seguinte
0

Sim, o eu é tedioso. Mas é melhor?

class Test:

    def __init__(_):
        _.test = 'test'

    def run(_):
        print _.test

fonte
10
_tem um significado especial no shell Python, onde contém o último valor retornado. É seguro usá-lo assim, mas potencialmente confuso; Eu evitaria isso.
Cairnarvon
Sem isso não melhor, mas porque não é uma única letra em vez, por exemplo s, ou m(para imitar C ++)
javadba
0

From: Self Hell - Funções mais stateful.

... uma abordagem híbrida funciona melhor. Todos os métodos de classe que realmente calculam devem ser movidos para fechamentos, e as extensões para limpar a sintaxe devem ser mantidas nas classes. Coloque os fechamentos em classes, tratando a classe como um espaço para nome. Os fechamentos são essencialmente funções estáticas e, portanto, não exigem selfs *, mesmo na classe ...

user5554473
fonte
Os fechamentos são adequados para pequenos casos de uso, mas o uso estendido deles aumentará bastante a sobrecarga de memória de um programa (como você está usando OO baseado em protótipo em vez de OO baseado em classe - portanto, cada objeto requer seu próprio conjunto de funções mantendo um conjunto comum de funções em uma classe). Além disso, ele impedirá que você seja capaz de usar métodos de magia / dunder (por exemplo, __str__e similares), uma vez que esses métodos são invocados de maneira diferente dos métodos normais.
Dunes
0

Eu acho que seria mais fácil e mais legível se houvesse uma declaração "membro", assim como existe "global", para que você possa dizer ao intérprete quais são os objetos membros da classe.

Pablo Villar
fonte