Re: [python-brasil] Re: Invocando método da super classe

495 views
Skip to first unread message

Pedro Werneck

unread,
Nov 25, 2006, 10:39:03 AM11/25/06
to python...@yahoogrupos.com.br
On Sat, 25 Nov 2006 10:10:31 -0200
"Luciano Ramalho" <ram...@gmail.com> wrote:

> Faltou um pequeno ***detalhe***, claro:
>
> class A:
> def spam():
> ...
>
> class B(A): # ***detalhe***
> def spam():
> ...
> super.spam() # é o que eu gostaria de fazer
> ...


E um outro detalhe não muito pequeno, mas importante... se você se
propõe a usar super(), não use herança múltipla se você não puder
fazê-lo usando super() consistentemente em todo lugar.

Por exemplo, com métodos de bultins, você vai ter de usar super() na
classe base, por exemplo em A.__init__() onde A é subclasse direta de
object(), mesmo sabendo que object.__init__() não faz nada.

Já comentei isso aqui na lista algum tempo atrás:


http://article.gmane.org/gmane.comp.python.brasil/19265

--
Pedro Werneck


========================================================
Antes de enviar sua mensagem dê uma lida em:
http://www.pythonbrasil.com.br/moin.cgi/AntesDePerguntar
========================================================
Links do Yahoo! Grupos

<*> Para visitar o site do seu grupo na web, acesse:
http://br.groups.yahoo.com/group/python-brasil/

<*> Para sair deste grupo, envie um e-mail para:
python-brasi...@yahoogrupos.com.br

<*> O uso que você faz do Yahoo! Grupos está sujeito aos:
http://br.yahoo.com/info/utos.html


Rafael SDM Sierra

unread,
Nov 25, 2006, 7:17:43 AM11/25/06
to python...@yahoogrupos.com.br
On 11/25/06, Luciano Ramalho <ram...@gmail.com> wrote:
>
> Qual é o modo "canônico" de implementar um método em uma sub-classe
> que sobrescreve mas invoca o mesmo método na super-classe?

>
> class A:
> def spam():
> ...
>
> class B:

> def spam():
> ...
> super.spam() # é o que eu gostaria de fazer


Então faça:
class B(A):
def spam(self):
super(B,self).spam()

MAS!!! a classe A precisa extender a classe object

> ...
>
> Lembro vagamente que antes existia um jeito meio horroso de fazer
> isso, que envolvia nomear explicitamente a super-classe (em vez de
> usar um nome genérico como "super" que existe em outras linguagens).
>
> Isto mudou? Enfim, qual a melhor forma de fazer isto em Python 2.5?
>
> [ ]s
> Luciano


>
>
> ========================================================
> Antes de enviar sua mensagem dê uma lida em:
> http://www.pythonbrasil.com.br/moin.cgi/AntesDePerguntar
> ========================================================
> Links do Yahoo! Grupos
>
>
>


--
SDM Underlinux
http://stiod.wordpress.com
Membro da equipe UnderLinux
--
PEP-8
There is only 2 kinds of peoples in the world, who know English, and I. oO


[As partes desta mensagem que não continham texto foram removidas]

Luciano Ramalho

unread,
Nov 25, 2006, 10:25:37 AM11/25/06
to python...@yahoogrupos.com.br
On 11/25/06, Rafael SDM Sierra <s...@underlinux.com.br> wrote:
> Então faça:
> class B(A):
> def spam(self):
> super(B,self).spam()

Valeu, Rafael!

Apenas registrando para referência futura, o exemplo fica assim
(refatorado no espírito da mitologia Monty):

<code>
class Guerreiro(object):
def __init__(self, nome):
self.__nome = nome

def urrar(self):
return self.__nome+': Ni!'

class Cavaleiro(Guerreiro):
def urrar(self):
urro = super(Cavaleiro,self).urrar()
return urro + ' Nii!'

g = Guerreiro('Negro')
print g.urrar()
# Negro: Ni!

c = Cavaleiro('Galahad')
print c.urrar()
# Galahad: Ni! Nii!
</code>

Agora, vamos combinar que super(Cavaleiro,self).urrar() é horrível
também (mas pelo menos eu não tenho que me referir à super-classe pelo
nome, como antigamente).

Esta sintaxe é um caso agudo daquela doença crônica da declaração
explícita do parâmetro self nos métodos. Eu considero esta
inconsistência (def spam(self, arg) VERSUS o.spam(arg) ) a coisa mais
feia do Python, e até hoje não fui capaz de entender uma explicação
que a justifique (mas admito que a culpa de não entender pode ser
minha limitação intelectual e não da falta de explicações).

Deixando de lado a sintaxe feia, parece que a semântica também não é
lá essas coisas, ao menos do ponto de vista da "usabilidade"
(entendento programadores como usuários da linguagem). Pelo menos é o
que diz esse cara:

http://fuhm.net/super-harmful/

[ ]s
Luciano


========================================================
Antes de enviar sua mensagem dê uma lida em:
http://www.pythonbrasil.com.br/moin.cgi/AntesDePerguntar
========================================================
Links do Yahoo! Grupos

<*> Para visitar o site do seu grupo na web, acesse:

Rafael SDM Sierra

unread,
Nov 25, 2006, 11:00:34 AM11/25/06
to python...@yahoogrupos.com.br
On 11/25/06, Luciano Ramalho <ram...@gmail.com> wrote:
>
> Agora, vamos combinar que super(Cavaleiro,self).urrar() é horrível
> também (mas pelo menos eu não tenho que me referir à super-classe pelo
> nome, como antigamente).
>
Hehehe...eu concordo que realmente seja muito feio...maaass....e quando voce
tiver o seguinte:

>>> class A(object):
... def nada(self):
... pass
...
>>> class B(object):
... def nada(self):
... pass
...
>>> class C(B,A):
... def nada(self):
... # Omg!!
... pass
...
>>>

Como voce acessaria a funcao nada da super-classe A, e da B? por isso ainda
não vejo como fugir de ter que especificar de que classe-pai voce quer
chamar a funcao a não ser falar qual é, seja atraves do antigo "A.nada()" ou
do novo "super(A, self).nada()"

--
SDM Underlinux
http://stiod.wordpress.com
Membro da equipe UnderLinux
--
PEP-8
There is only 2 kinds of peoples in the world, who know English, and I. oO


[As partes desta mensagem que não continham texto foram removidas]

========================================================

Pedro Werneck

unread,
Nov 25, 2006, 11:54:59 AM11/25/06
to python...@yahoogrupos.com.br
On Sat, 25 Nov 2006 14:00:34 -0200

"Rafael SDM Sierra" <s...@underlinux.com.br> wrote:

É o que comentei no outro e-mail... super() chama os métodos seguintes
da MRO, então mesmo que seja uma subclasse direta de object, você tem de
usar super() nela. O problema é que você acaba com uma hierarquia em
diamante de qualquer forma, e se for como o caso aqui, de um método que
não existe em object(), realmente a coisa complica...

Nesse seu exemplo, a MRO é:

(<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <type
'object'>)

Ou seja, quando você chama C.nada, super() primeiro acessa o atributo em
B, o super() em B.nada chamada o de A, e se fosse manter o uso
consistente, colocando uma chamada em A.nada, ele tentaria chamar
object.nada(), que não existe e dá AttributeError:

>>> class A(object):
... def nada(self):

... super(A, self).nada()
... print 'A'


...
>>> class B(object):
... def nada(self):

... super(B, self).nada()
... print 'B'
...
>>> class C(B, A):
... def nada(self):
... super(C, self).nada()
... print 'C'
...
>>> o = C()
>>> o.nada()
Traceback (most recent call last):
...
AttributeError: 'super' object has no attribute 'nada'
>>>


Se for uma hierarquia estática, a classe A não será usada em lugar
nenhum, você pode tirar o super() de lá e deixar só em B:

>>> class A(object):
... def nada(self):

... print 'A'


...
>>> class B(object):
... def nada(self):

... super(B, self).nada()
... print 'B'
...
>>> class C(B, A):
... def nada(self):
... super(C, self).nada()
... print 'C'
...
>>> o = C()
>>> o.nada()
A
B
C
>>>


Se não puder, você pode manter o super(), mas usar um bloco try/except
para capturar AttributeError nas subclasses diretas de object() para
manter a consistência.

>>> class A(object):
... def nada(self):

... try:
... super(A, self).nada()
... except AttributeError:
... pass
... print 'A'


...
>>> class B(object):
... def nada(self):

... try:
... super(B, self).nada()
... except AttributeError:
... pass
... print 'B'
...
>>> class C(B, A):
... def nada(self):
... super(C, self).nada()
... print 'C'
...
>>> o = C()
>>> o.nada()
A
B
C
>>>

Na pior das hipóteses, em que você não puder ou não quiser nenhuma das
soluções acima, você pode usar uma classe base como interface que defina
os métodos seus sem fazer nada, terminando com uma hierarquia em
diamante mas com o método presente em todas as classes:

>>> class Base(object):


... def nada(self):
... pass
...

>>> class A(Base):
... def nada(self):
... super(A, self).nada()
... print 'A'
...
>>> class B(Base):
... def nada(self):
... super(B, self).nada()
... print 'B'
...
>>> class C(B, A):
... def nada(self):
... super(C, self).nada()
... print 'C'
...
>>> o = C()
>>> o.nada()
A
B
C
>>>


Resumindo, só use super() com herança múltipla se realmente precisar e
souber o que está fazendo.


Abraços,

--
Pedro Werneck

Pedro Werneck

unread,
Nov 25, 2006, 10:55:14 AM11/25/06
to python...@yahoogrupos.com.br
On Sat, 25 Nov 2006 13:25:37 -0200
"Luciano Ramalho" <ram...@gmail.com> wrote:

> Agora, vamos combinar que super(Cavaleiro,self).urrar() é horrível
> também (mas pelo menos eu não tenho que me referir à super-classe pelo
> nome, como antigamente).

Mais ou menos... você também pode usar super(Cavaleiro).urrar(self) se
preferir. Não acho horrível... é a solução compatível com a natureza
dinâmica da linguagem.


> Esta sintaxe é um caso agudo daquela doença crônica da declaração
> explícita do parâmetro self nos métodos. Eu considero esta
> inconsistência (def spam(self, arg) VERSUS o.spam(arg) ) a coisa mais
> feia do Python, e até hoje não fui capaz de entender uma explicação
> que a justifique (mas admito que a culpa de não entender pode ser
> minha limitação intelectual e não da falta de explicações).

Bom... def spam(self, arg) X o.spam(arg) não é uma inconsistência. Os
três elementos estão presentes nas duas versões... a instância, a
função/método e o argumento/parâmetro.

Entender porque isso ocorre não é limitação intelectual, mas como diria
o Osvaldo, exige um nível de iluminação na linguagem. A minha dica para
isso continua a mesma. Entender por que isso ocorre:

>>> class C(object):
... def m(self):
... pass
...
>>> o = C()
>>> o.m is o.m
False


> Deixando de lado a sintaxe feia, parece que a semântica também não é
> lá essas coisas, ao menos do ponto de vista da "usabilidade"
> (entendento programadores como usuários da linguagem). Pelo menos é o
> que diz esse cara:
>
> http://fuhm.net/super-harmful/

É o que eu comentei no outro e-mail. Se estiver usando herança múltipla,
não use super() se não souber muito bem o que está fazendo.


Abraços,

--
Pedro Werneck


Pedro Werneck

unread,
Nov 26, 2006, 10:24:50 AM11/26/06
to python...@yahoogrupos.com.br
On Sat, 25 Nov 2006 19:39:20 -0200
"Luciano Ramalho" <ram...@gmail.com> wrote:

> On 11/25/06, Pedro Werneck <pedro....@terra.com.br> wrote:
> > Bom... def spam(self, arg) X o.spam(arg) não é uma inconsistência.
> > Os três elementos estão presentes nas duas versões... a instância, a
> > função/método e o argumento/parâmetro.
>

> Visualmente, ao menos para mim, é uma inconsistência. Eu vejo dois
> parâmetros na definição do método e um na chamada. Se o parâmetro self
> fosse implícito na definição do método, ou fosse acessível de alguma
> outra maneira (como em tudo quanto é linguagem), este problema não
> ocorreria. A justificativa para o self explícito é que eu nunca
> entendi.


>
> > Entender porque isso ocorre não é limitação intelectual, mas como
> > diria o Osvaldo, exige um nível de iluminação na linguagem. A minha
> > dica para isso continua a mesma. Entender por que isso ocorre:
> >
> > >>> class C(object):
> > ... def m(self):
> > ... pass
> > ...
> > >>> o = C()
> > >>> o.m is o.m
> > False
>

> UAU! Grande Werneck, me perdôe mas eu tenho que perguntar: porquê isso
> ocorre?


Bom... tem um tópico longo explicando isso por aí, mas... tentando
resumir a história. O comando 'class' cria um namespace privado para
você definir os atributos dela, entre eles os métodos, mas o objeto
função que você cria na definição de classe e o método que você acessa
depois como atributo da classe ou do objeto não são o mesmo objeto.

O método é um descriptor usado como wrapper para a função. Quando você
acessa ele como atributo, o método __get__ do descriptor é chamado para
retornar uma instância do método, tendo como atributos a função
original, a instância da prória classe e a classe (.im_func, .im_self,
.im_class).

Dependendo do tipo do método, o método __call__ dele vai fazer alguma
coisa... nos métodos normais, __call__(self, *args, **kwds) vai fazer
self.im_func(self.im_self, *args, **kwds), ou seja, passar a instância
como primeiro argumento. Em staticmethod() ele não passa nada, e em
classmethod() ele passa a classe contida em .im_class.

Resumindo, quando eu acesso o.m ali, não estou acessando diretamente a
função m que criei lá dentro, e sim um descriptor que retorna um objeto
que serve de wrapper para a função, e que dependendo do tipo, pode ou
não passar algo como primeiro argumento, em 99% dos casos, a instância
para o self, que você acha inconsistente.

O efeito colateral dessa implementação é que como a cada acesso ao
atributo método o interpretador tem de criar uma nova instância de
MethodType, quando você tenta acessá-lo duas vezes na mesma expressão,
são criadas duas instâncias diferentes e aparece esse probleminha aí em
cima... Python violando a Lei da Ientidade, a mais básica da lógica.

Entendendo isso, a justificativa para ser feito dessa maneira e não de
outra qualquer é a simplicidade... por mais difícil que seja de
entender para alguns, essa é a maneira mais simples de implementar
métodos na linguagem.

Descartando o argumento de que explícito é melhor, ter a instância
acessível implicitamente de alguma forma dentro do método não é algo tão
simples assim. Exigiria alterações drásticas no tipo função, uma função
que você definisse dentro do comando class seria algo de um tipo
diferente da função definida em outro escopo qualquer, o que também foge
muito da filosofia da linguagem. Para tentar enfiar uma referência ao
self automaticamente dentro do namespace da função (tirando o detalhe de
que aí sim seria inconsistente, contando os elementos como citei acima),
você acabaria tendo de usar descriptors da mesma forma atual, mas teria
de alterar o byte-code original de cada função para conseguir inserir a
referência ali dentro... fora a complexidade de fazer essa alteração, o
impacto na performance recriando a função a cada chamada seria uma
pancada feia.


Resumindo, a justificativa para o self explícito é de que explícito é
melhor do que implícito, simples é melhor do que complexo, complexo é
melhor do que complicado e deve haver uma, e de preferência apenas uma
maneira óbvia de fazer algo.

É complicado de entender, mas assim que você entende, vê que é mais
simples do que parece...

Luciano Ramalho

unread,
Nov 26, 2006, 11:12:41 AM11/26/06
to python...@yahoogrupos.com.br
Werneck, muito grato pela excelente explicação, e me perdôe por não
ter ido atrás dela no histórico da lista.

Graças à sua explicação do MethodType pude endender pela primeira vez
como se justifica passagem explícita do self, e o mistério do "o.m is
not o.m".

Abusando da matemática para um pouco de filosofia de botequim: Gödel
já sentenciou que todos os sistema são inconsistentes ou incompletos.
Para serem práticas, nossas linguagens de programação têm que ser
inconsistentes em algum nível. Python é uma das linguagens mais
consistentes que eu conheci (só Lisp e Smalltalk são mais). No caso da
declaração do parâmetro self e da quebra do princípio da identidade
nos métodos, Guido aparentemente optou por uma inconsistência no nível
do uso da linguagem para tornar mais consistente a implementação.

Como eu disse em outra mensagem, Ruby é uma linguagem mais consistente
em alguns aspectos relativos à orientação a objetos, e menos em
outros. Aparentemente, no caso de Ruby, o custo de manter uma certa
ilusão de consistência no nível do uso tem causado problemas sérios no
nível da implementação. Quem já estudou o interpretador Ruby diz que
ele é cheio de vudu, e talvez por isso a versão 2.0 do Ruby é
vaporware há quase 10 anos...

Não é trivial escolher entre facilitar a vida do usuário e simplificar
a implementação, quando estes requisitos entram em conflito. Muitas
linguagens, como Java e C, escolhem a segunda em detrimento da
primeira. Os autores do Ruby priorizam a primeira, e a genialidade do
Guido está em buscar sempre o equilíbrio estes dois aspectos.

Valeu, Rafael e Werneck, pela grande lição!

[ ]s
Luciano

Pedro Werneck

unread,
Nov 27, 2006, 3:58:19 PM11/27/06
to python...@yahoogrupos.com.br
On Sun, 26 Nov 2006 14:12:41 -0200
"Luciano Ramalho" <ram...@gmail.com> wrote:

> Werneck, muito grato pela excelente explicação, e me perdôe por não
> ter ido atrás dela no histórico da lista.

Na verdade nem eu estou achando de novo, mas se me lembro bem foi uma
discussão com o Luciano Pacheco, explicando porque property() e outros
descriptors têm de ser definidos no escopo da classe, e não
dinamicamente em uma instância.

> Graças à sua explicação do MethodType pude endender pela primeira vez
> como se justifica passagem explícita do self, e o mistério do "o.m is
> not o.m".

Ótimo... na última PyCon-Brasil houve uma discussão frustrante em que
expliquei isso para alguns e ninguém entendeu. Geralmente só o pessoal
com mais experiência na linguagem e que já se sentem bem à vontade com
ela entendem facilmente.


> Abusando da matemática para um pouco de filosofia de botequim: Gödel
> já sentenciou que todos os sistema são inconsistentes ou incompletos.
> Para serem práticas, nossas linguagens de programação têm que ser
> inconsistentes em algum nível. Python é uma das linguagens mais
> consistentes que eu conheci (só Lisp e Smalltalk são mais). No caso da
> declaração do parâmetro self e da quebra do princípio da identidade
> nos métodos, Guido aparentemente optou por uma inconsistência no nível
> do uso da linguagem para tornar mais consistente a implementação.

Quanto ao self explícito, repito: não é uma inconsistência. É algo
diferente do que usuários de outras linguagens esperam, mas é
perfeitamente consistente. O mesmo número de elementos aparece tanto na
definição quando na chamada. Quando à identidade dos métodos, é uma
inconsistência válida, pois duvido que incomode alguém ou já tenha
causado algum problema... quem já compara a identidade de um método com
ele mesmo para fazer algo ???


> Como eu disse em outra mensagem, Ruby é uma linguagem mais consistente
> em alguns aspectos relativos à orientação a objetos, e menos em
> outros. Aparentemente, no caso de Ruby, o custo de manter uma certa
> ilusão de consistência no nível do uso tem causado problemas sérios no
> nível da implementação. Quem já estudou o interpretador Ruby diz que
> ele é cheio de vudu, e talvez por isso a versão 2.0 do Ruby é
> vaporware há quase 10 anos...

Conheço muito pouco Ruby para opinar a respeito, mas conheço os extremos
a que chegam nessa obsessão por manter consistência. É só pensar que
algo como o problema da identidade dos métodos em Python, que não tem
qualquer consequência real, com os princípios que regem Ruby seria
provavelmente inaceitável e teria de usar uma solução bem mais
complicada.


> Não é trivial escolher entre facilitar a vida do usuário e simplificar
> a implementação, quando estes requisitos entram em conflito. Muitas
> linguagens, como Java e C, escolhem a segunda em detrimento da
> primeira. Os autores do Ruby priorizam a primeira, e a genialidade do
> Guido está em buscar sempre o equilíbrio estes dois aspectos.

Voluntariamente ou não, não sei... mas Python também parece priorizar a
liberdade total do usuário para fazer o que quer. É comum topar com um
desafio em outras linguagens e achar que não há como fazer, mas em
Python isso simplesmente não acontece. Você se pergunta como fazer algo
(e se deve usar), mas é raro topar com algo em Python e dizer
imediatamente: não há como fazer isso.


Abraços,

--
Pedro Werneck

Rodrigo Senra

unread,
Dec 3, 2006, 1:53:11 PM12/3/06
to python...@yahoogrupos.com.br

On 25Nov 2006, at 1:39 PM, Pedro Werneck wrote:

> On Sat, 25 Nov 2006 10:10:31 -0200
> "Luciano Ramalho" <ram...@gmail.com> wrote:
>
>> Faltou um pequeno ***detalhe***, claro:

> E um outro detalhe não muito pequeno, mas importante... se você se
> propõe a usar super(), não use herança múltipla se você não puder
> fazê-lo usando super() consistentemente em todo lugar.

Puxa, perdi grande parte da ação enquanto estava fora.

Um fenômeno relevante para esta discussão do super() é a forma
como nós usamos hierarquias de classes. Uma forma de enxergar
os padrões de uso é a seguinte:

1) criação de hierarquias de especialização/generalização
2) reuso de código

1 e 2 podem acontecer juntos, mas não necessariamente. Muitas vezes
2) ocorre sem a intenção de 1). Ou seja, uma classe é criada sem que
seja um sub-tipo ou uma especialização de sua superclasse direta.
Nestes caso, pode ser ferido o princípio da substitutabilidade[1]
de Baraba Liskov.

Esse blá blá blá é relevante, pois acabamos criando uma hierarquia de
classes sem ter essa preocupação "purista", de que qualquer instãncia de
uma sub-classe possa *sempre* ser utilizada onde seria utilizada uma
instãncia de suas classes ancestrais. (Em suma, vc deve ser capaz de
fazer
tudo que seu pai faria, mas a recíproca é falsa).

O efeito colateral desta preocupação purista seria a compatibilidade dos
inicializadores __init__, que teriam que ser declarados com uma
variante de
def __init__(self, *arg, **kw). Isso resolveria __parte__ do problema
com super,
a parte da compatibilidade, a feiúra (como lembrou o Ramalho) não tem
jeito.

O Werneck tocou nesse ponto, eu só queria adicionar essa questão
teórico-filosófica,
que IMVHO origina os problemas práticos citados.

[1] http://en.wikipedia.org/wiki/Liskov_Substitution_Principle

PS: Uma mera curiosidade, eu acabei conhecendo a Barbara Liskov ao vivo
e em cores ;o) Eu conto esta história, se alguém quiser saber,
na próxima
PyConBrasil :oP

Abração,
Senra


Rodrigo Senra
______________
rsenra @ acm.org
http://rodrigo.senra.nom.br

Luciano Ramalho

unread,
Dec 3, 2006, 2:57:39 PM12/3/06
to python...@yahoogrupos.com.br
On 12/3/06, Rodrigo Senra <rse...@acm.org> wrote:
> Um fenômeno relevante para esta discussão do super() é a forma
> como nós usamos hierarquias de classes. Uma forma de enxergar
> os padrões de uso é a seguinte:
>
> 1) criação de hierarquias de especialização/generalização
> 2) reuso de código

Acho que dá para incluir um 3):

3) componentização através de interfaces padronizadas

Porque além da implementação, as sub-classes herdam a interface das
super-classes. Em Java existe herança múltipla de interfaces (mas sem
a implementação). Em Python usamos mix-ins para herdar interfaces e
implementações. Vale dizer que é mais fácil reusar implementações de
interfaces em linguagens de tipagem dinâmica como Python, Smalltalk e
Ruby do que em Java.

A famosa arquitetura de componentes do Zope 3 é toda baseada em
interfaces também.

> 1 e 2 podem acontecer juntos, mas não necessariamente. Muitas vezes
> 2) ocorre sem a intenção de 1). Ou seja, uma classe é criada sem que
> seja um sub-tipo ou uma especialização de sua superclasse direta.
> Nestes caso, pode ser ferido o princípio da substitutabilidade[1]
> de Baraba Liskov.

CLASSES NÃO SÃO TIPOS

Agora, uma perspectiva interessante sobre o que diz a Liskov: ela fala
de tipos e sub-tipos, e não de classes e sub-classes. Estou estudando
Ruby e naquela comunidade existe um mantra do David Thomas: "Classes
não são tipos!". Objetos do mesmo tipo podem ser usados de forma
intercambiável, mas não necessariamente precisam ter uma super-classe
comum, basta implementarem uma mesma interface. É o que o Dave Thomas
apelidou de "duck typing" ou literalmente tipagem-pato: se anda como
um pato e grasna como um pato, podemos tratar como se fosse um pato.
Em Python temos a mesma idéia.

EXEMPLO EM PYTHON

Em Python, listas e strings implementam muitas das mesmas operações, e
por isso podem ser consideradas como sendo do mesmo tipo. A
documentação até se refere a ambos como "sequências", mas a única
super-classe comum a ambas é object (já é ou ainda vai ser um dia?). E
object não é do mesmo tipo que strings e lists, porque não implementa
o protocolo de sequência.

TERMINOLOGIA

Aqui eu usei o termo de Smalltalk (protocolo). Em Python qual seria o
jargão correto? Porque o que chamamos de sequência não é uma interface
explícitamente declarada, mas é uma interface, porém usar este termo é
no caso é bem ruim. Dei uma olhada na Library Reference e parece que
ela se esforça para não dar um nome ao conceito exemplificado pelo
rótulo "sequência". Em Smalltalk é um protocolo.


[ ]s
Luciano

Rodrigo Senra

unread,
Dec 3, 2006, 7:38:39 PM12/3/06
to python...@yahoogrupos.com.br

On 3Dec 2006, at 5:57 PM, Luciano Ramalho wrote:

> On 12/3/06, Rodrigo Senra <rse...@acm.org> wrote:
>> Um fenômeno relevante para esta discussão do super() é a forma
>> como nós usamos hierarquias de classes. Uma forma de enxergar
>> os padrões de uso é a seguinte:
>>
>> 1) criação de hierarquias de especialização/generalização
>> 2) reuso de código
>
> Acho que dá para incluir um 3):
>
> 3) componentização através de interfaces padronizadas

Sem dúvida.

> CLASSES NÃO SÃO TIPOS

Há controvérsias =)
Classes podem ser definidas como mecanismos para se extender os
tipos primitivos de uma linguagem. E por extensão, podem ser
denominadas tipos.

Python concorda de certa maneira com esta visão:
<code>
>>> class A(object):
... pass
...
>>> a = A()
>>> type(a)
<class '__main__.A'>
</code>

Eu concordo que diferentes linguagens oferecem diferentes interpretações
e materialização para o conceito de classes.

Inclusive Python inicialmente tratava tipos e classes distintamente,
mas ao longo de sua evolução optou pela unificação entre
os dois conceitos a partir de Python 2.2, com o célebre
"Unifying types and classes in Python 2.2" [1]

Portanto, no contexto de Python >=2.2, eu discordo e acho que
podemos considerar classes, tipos.


> Agora, uma perspectiva interessante sobre o que diz a Liskov: ela fala
> de tipos e sub-tipos, e não de classes e sub-classes.

Existe uma longa discussão entre a dicotomia entre tipo e classe.
Em particular [2] é bastante esclarecedor e dentro do nosso contexto
de discussão, inclusive menciona Python e acredito seja aderente ao seu
argumento.

"""
It's tempting to say that it makes no difference at all, that we
should use inheritance any time we want substitutability, but that's
terribly impractical and people who use dynamic languages just don't
work that way. My experience in dynamic languages isn't very
extensive, but the thing I notice is that you get a little bit of
slack in Ruby, Python, and Smalltalk. You start out with a piece of
code that seems to expect an object of a particular class, and then
you look at it and realize that it only expects a few methods. At
that point, you've noticed a generalization.
"""

> Estou estudando Ruby e naquela comunidade existe um mantra do David
> Thomas: "Classes

> não são tipos!". Objetos do mesmo tipo...

Neste caso o que são objetos do mesmo tipo ? tipo = protocolo = API ?

> ...podem ser usados de forma intercambiável,

O intercâmbio pressupõe compatibilidade semântica também ?

> mas não necessariamente precisam ter uma super-classe
> comum, basta implementarem uma mesma interface.

Esta é a definição original de "tipo abstrato de dados", do qual
só se conhecem as operações aplicáveis. Classes expandiram esse
conceito adicionando herança e polimorfismo de inclusão.

Herança e polimorfismo podem ser ignorados, e
classes podem ser usadas meramente como tipos abstratos de dados.
Naturalmente linguagens estaticamente tipadas (como Java e C++)
diminuem a flexibilidade dessa abordagem, enquanto linguagens
dinâmicas (como Python e Ruby) permitem o estilo duck typing.

> É o que o Dave Thomas
> apelidou de "duck typing" ou literalmente tipagem-pato: se anda como
> um pato e grasna como um pato, podemos tratar como se fosse um pato.
> Em Python temos a mesma idéia.

Concordo.

Meu argumento é: respeitar o princípio da substituição está implícito
em duck typing, mas se respeitado em hierarquias de especialização/
generalização
na OO tradicional minimiza (quiça resolve) o problema das classes
cooperativas.

[1] http://www.python.org/download/releases/2.2/descrintro/
[2] http://butunclebob.com/
ArticleS.MichaelFeathers.LiskovSubstitutionInDynamicLanguages

Abração,
Senra


Rodrigo Senra
______________
rsenra @ acm.org
http://rodrigo.senra.nom.br

Luciano Ramalho

unread,
Dec 4, 2006, 9:24:18 AM12/4/06
to python...@yahoogrupos.com.br
On 12/3/06, Rodrigo Senra <rse...@acm.org> wrote:
> Esta é a definição original de "tipo abstrato de dados", do qual
> só se conhecem as operações aplicáveis. Classes expandiram esse
> conceito adicionando herança e polimorfismo de inclusão.

Valeu o toque sobre ADTs, Senra.

Em relação ao uso de Classes para definir tipos, é algo
indubitavelmente útil e largamente utilizado, claro. Mas, como dizia o
Milton Friedman, "não existe almoço de graça".

A hierarquia de classes traz consigo a limitação de todas as
hierarquias: o que fazer com algo que precisa ser encaixado em dois
lugares? Herdar métodos de apenas uma super-classe é muito limitante
às vezes. Herança múltipla ameniza este problema, mas cria um monte de
outros.

O mecanismo de aquisição do Zope 2 foi um experimento ousado para
contornar as limitações da herança, mas parece que o consenso é de que
ele trouxe uma carga excessiva de problemas adicionais também. Tenho a
impressão que a tal da AOP (Aspect Oriented Programming) é outra
tentativa de permitir o compartilhamento de operações indendentemente
da hierarquia de classes.

A biblioteconomia vem se debatendo há muito tempo com grandes
hierarquias, e o grande matemático-bibliotecário indiano S. R.
Ranganathan já matou a charada há uns 70 anos (embora a maioria dos
bibliotecários ainda não tenha se dado conta, porque ele era indiano,
e não europeu ou americano). Ranganathan percebeu que uma única
hierarquia nunca é suficiente para organizar qualquer coleção
não-trivial de objetos.

Um dia a computação vai aprender esta lição também.

[ ]s
Luciano

Rodrigo Senra

unread,
Dec 4, 2006, 2:50:33 PM12/4/06
to python...@yahoogrupos.com.br

On 4Dec 2006, at 12:24 PM, Luciano Ramalho wrote:
> Em relação ao uso de Classes para definir tipos, é algo
> indubitavelmente útil e largamente utilizado, claro. Mas, como dizia o
> Milton Friedman, "não existe almoço de graça".
>
> A hierarquia de classes traz consigo a limitação de todas as
> hierarquias: o que fazer com algo que precisa ser encaixado em dois
> lugares?

Em geral, a resposta é fatorar e delegar. Acredito que o ponto
que vc esteja levantando seja: Ok, mas re-fatorar a hierarquia
muitas vezes é o indesejável.
E novamente concordo com vc: no free lunch.

Meu sentimento é que hierarquias de classe modelam bem conceitos
que sejam naturalmente hierárquicos no mundo real, **mas** que sejam
hierarquias de especialização como: Animal -> mamífero -> roedor,
veículo -> avião-> jato -> F-14.

Já as hierarquias de subordinação (não necessariamente de
especialização)
estão mais sujeitas ao problema do "encaixe" que vc mencionou. Nestes
casos,
talvez seja melhor ter um pool de classes flat e criar um grafo de
delegação
entre as instâncias.

> O mecanismo de aquisição do Zope 2 foi um experimento ousado para
> contornar as limitações da herança, mas parece que o consenso é de que
> ele trouxe uma carga excessiva de problemas adicionais também.

Boa lembrança. Acabei só usando Acquisição dentro do Zope, pois lá
existia
um mecanismo de browse (a ZMI) imprescindível para que o programador
não se
perca nos "acquisition paths". E mesmo no Zope senti falta de um
mecanismo fácil
para depurar problemas de acquisição. Eu acho que o que "queimou" mais a
acquisição foi o DTML e os famigerados dtml-vars, mas isto já é uma
discussão
para outra thread ;o)

> Tenho a impressão que a tal da AOP (Aspect Oriented Programming) é
> outra
> tentativa de permitir o compartilhamento de operações indendentemente
> da hierarquia de classes.

Pois é, todavia se não me engana a memória a AOP trabalha em cima de um
modelo de características não-funcionais, que fossem ortogonais
(crosscut
no jargão AOP) ao resto da aplicação. Por exemplo: log, depuração,
persistência,
transação, ou seja que independem do domínio de atuação da aplicação.
Isso limita
um pouco a aplicabilidade. Infelizmente, a AOP ficou muito restrita
ao AspectJ e
Java. Vc sabe de outras ferramentas que tenham incorporado AOP ?
Eu sei que a Logilab (França) criou um módulo aop.py para Python, mas
não
acompanhei a evolução disso.


> A biblioteconomia vem se debatendo há muito tempo com grandes
> hierarquias, e o grande matemático-bibliotecário indiano S. R.
> Ranganathan já matou a charada há uns 70 anos (embora a maioria dos
> bibliotecários ainda não tenha se dado conta, porque ele era indiano,
> e não europeu ou americano). Ranganathan percebeu que uma única
> hierarquia nunca é suficiente para organizar qualquer coleção
> não-trivial de objetos.

E graças a este bizzu que vc me deu da classificação multi-facetada e
do Ranganathan vou tentar elaborar um projeto de pesquisa de doutorado
aplicando ontologias, classificação multi-facetada e organização de
info.
Isso ficou na geladeira desde aquela nossa conversa, devo resgatar o
assunto
ainda este mês e escrever uma proposta até dia 20 de dezembro.

Abração,
Senra


Rodrigo Senra
______________
rsenra @ acm.org
http://rodrigo.senra.nom.br

Reply all
Reply to author
Forward
0 new messages