it-swarm-pt.tech

Por que você precisa explicitamente ter o argumento "self" em um método Python?

Ao definir um método em uma classe em Python, é algo como isto:

class MyClass(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

Mas em algumas outras linguagens, como C #, você tem uma referência ao objeto ao qual o método está vinculado com a palavra-chave "this" sem declará-lo como um argumento no protótipo do método. 

Essa foi uma decisão de design de linguagem intencional em Python ou existem alguns detalhes de implementação que exigem a passagem de "self" como um argumento?

180
Readonly

Eu gosto de citar o Zen de Python de Peters. "Explícito é melhor que implícito."

Em Java e C++, 'this.' pode ser deduzido, exceto quando você tem nomes de variáveis ​​que tornam impossível deduzir. Então você às vezes precisa e às vezes não precisa.

O Python decide tornar isso explícito em vez de baseado em uma regra. 

Além disso, como nada é implícito ou assumido, partes da implementação são expostas. self.__class__, self.__dict__ e outras estruturas "internas" estão disponíveis de maneira óbvia.

88
S.Lott

É minimizar a diferença entre métodos e funções. Ele permite gerar facilmente métodos em metaclasses ou adicionar métodos em tempo de execução a classes pré-existentes.

por exemplo.

>>> class C(object):
...     def foo(self):
...         print "Hi!"
...
>>>
>>> def bar(self):
...     print "Bork bork bork!"
...
>>>
>>> c = C()
>>> C.bar = bar
>>> c.bar()
Bork bork bork!
>>> c.foo()
Hi!
>>>

Também (até onde eu sei) facilita a implementação do tempo de execução do Python.

55
Ryan

Eu sugiro que se deve ler O blog de Guido van Rossum sobre este tópico - Por que o self explícito tem que ficar .

Quando uma definição de método é decorada, não sabemos se devemos dar automaticamente um parâmetro 'self' ou não: o decorador pode transformar a função em um método estático (que não tem 'self'), ou um método de classe (que tem um tipo engraçado de self que se refere a uma classe em vez de uma instância), ou poderia fazer algo completamente diferente (é trivial escrever um decorador que implementa '@classmethod' ou '@staticmethod' no Python puro). Não há como, sem saber o que o decorador faz, se dotar o método que está sendo definido com um argumento 'self' implícito ou não.

Eu rejeito hacks como o special-casing '@classmethod' e '@staticmethod'.

51
bhadra

O Python não força você a usar o "eu". Você pode dar o nome que quiser. Você só precisa lembrar que o primeiro argumento em um cabeçalho de definição de método é uma referência ao objeto.

16
Victor Noagbodji

Também permite que você faça isso: (em suma, invocar Outer(3).create_inner_class(4)().weird_sum_with_closure_scope(5) retornará 12, mas fará isso da maneira mais louca.

class Outer(object):
    def __init__(self, outer_num):
        self.outer_num = outer_num

    def create_inner_class(outer_self, inner_arg):
        class Inner(object):
            inner_arg = inner_arg
            def weird_sum_with_closure_scope(inner_self, num)
                return num + outer_self.outer_num + inner_arg
        return Inner

Claro, isso é mais difícil de imaginar em linguagens como Java e C #. Ao tornar a auto-referência explícita, você está livre para se referir a qualquer objeto por essa auto-referência. Além disso, é mais difícil fazer isso com as classes em tempo de execução nas linguagens mais estáticas - não é necessariamente bom ou ruim. É só que o eu explícito permite que toda essa loucura exista.

Além disso, imagine o seguinte: Gostaríamos de personalizar o comportamento dos métodos (para criar perfis ou alguma magia negra maluca). Isso pode nos levar a pensar: e se tivéssemos uma classe Method cujo comportamento poderíamos ignorar ou controlar?

Bem, aqui está:

from functools import partial

class MagicMethod(object):
    """Does black magic when called"""
    def __get__(self, obj, obj_type):
        # This binds the <other> class instance to the <innocent_self> parameter
        # of the method MagicMethod.invoke
        return partial(self.invoke, obj)


    def invoke(magic_self, innocent_self, *args, **kwargs):
        # do black magic here
        ...
        print magic_self, innocent_self, args, kwargs

class InnocentClass(object):
    magic_method = MagicMethod()

E agora: InnocentClass().magic_method() irá agir como esperado. O método será vinculado com o parâmetro innocent_self para InnocentClass e com o magic_self para a ocorrência de MagicMethod. Estranho hein? É como ter duas palavras-chave this1 e this2 em linguagens como Java e C #. Magia como essa permite que frameworks façam coisas que de outra forma seriam muito mais verbosas.

Mais uma vez, não quero comentar sobre a Ética deste material. Eu só queria mostrar coisas que seriam mais difíceis de fazer sem uma auto-referência explícita.

6
vlad-ardelean

Eu acho que a verdadeira razão, além de "O Zen do Python", é que as funções são cidadãos de primeira classe em Python. 

Que essencialmente os torna um objeto. Agora, a questão fundamental é se as suas funções também são objeto, então, no paradigma Orientado a objetos, como você envia mensagens para Objetos quando as próprias mensagens são objetos? 

Parece um problema de ovo de galinha, para reduzir esse paradoxo, a única maneira possível é passar um contexto de execução para métodos ou detectá-lo. Mas como o Python pode ter funções aninhadas, seria impossível fazê-lo, pois o contexto de execução mudaria para funções internas. 

Isso significa que a única solução possível é passar explicitamente "eu" (o contexto da execução).

Então eu acredito que é um problema de implementação, o Zen veio muito mais tarde.

2
pankajdoharey

Eu acho que tem a ver com o PEP 227:

Nomes no escopo da classe não estão acessíveis. Os nomes são resolvidos no escopo da função de inclusão mais interna . Se uma definição de classe ocorre em uma cadeia De escopos aninhados, o processo de resolução ignora as definições de classe . Essa regra evita interações ímpares entre atributos de classe E acesso a variáveis ​​locais. Se uma operação de ligação de nomes Ocorrer em uma definição de classe, ela criará um atributo no objeto de classe Resultante. Para acessar essa variável em um método, ou em uma função Aninhada em um método, uma referência de atributo deve ser usada, Via auto ou por meio do nome da classe.

2
daole

Como explicado em self em Python, Demystified

qualquer coisa como obj.meth (args) se torna Class.meth (obj, args). O processo de chamada é automático enquanto o processo de recebimento não é (é explícito). Esta é a razão pela qual o primeiro parâmetro de uma função na classe deve ser o próprio objeto. 

class Point(object):
    def __init__(self,x = 0,y = 0):
        self.x = x
        self.y = y

    def distance(self):
        """Find distance from Origin"""
        return (self.x**2 + self.y**2) ** 0.5

Invocações:

>>> p1 = Point(6,8)
>>> p1.distance()
10.0

init () define três parâmetros, mas acabamos de passar dois (6 e 8). Similarmente distance () requer um, mas zero argumentos foram passados. 

Por que o Python não está reclamando sobre esta incompatibilidade de número de argumento?

Geralmente, quando chamamos um método com alguns argumentos, a função de classe correspondente é chamada colocando o objeto do método antes do primeiro argumento. Então, qualquer coisa como obj.meth (args) se torna Class.meth (obj, args). O processo de chamada é automático enquanto o processo de recebimento não é (é explícito)

Esta é a razão pela qual o primeiro parâmetro de uma função na classe deve ser o próprio objeto. Escrever este parâmetro como self é meramente uma convenção. Não é uma palavra-chave e não tem um significado especial no Python. Poderíamos usar outros nomes (como este), mas eu sugiro fortemente que você não faça isso. O uso de nomes diferentes de self é desaprovado pela maioria dos desenvolvedores e degrada a legibilidade do código ("Readability counts").
...
Em, o primeiro exemplo self.x é um atributo de instância, enquanto x é uma variável local. Eles não são os mesmos e estão em diferentes namespaces.

Self está aqui para ficar

Muitos se propuseram a tornar-se uma palavra-chave em Python, como esta em C++ e Java. Isso eliminaria o uso redundante do self explícito da lista de parâmetros formais em métodos. Embora essa ideia pareça promissora, não vai acontecer. Pelo menos não no futuro próximo. O principal motivo é a compatibilidade com versões anteriores. Aqui está um blog do criador do próprio Python explicando porque o eu explícito tem que ficar.

0
mon