Qual é a diferença entre classes de estilo antigo e novo em Python?
-
09-06-2019 - |
Pergunta
Qual é a diferença entre classes de estilo antigo e novo em Python?Quando devo usar um ou outro?
Solução
De http://docs.python.org/2/reference/datamodel.html#new-style-and-classic-classes :
Até o Python 2.1, as classes antigas eram o único tipo disponível para o usuário.
O conceito de classe (estilo antigo) não está relacionado ao conceito de tipo:se
x
é uma instância de uma classe de estilo antigo, entãox.__class__
designa a classe dex
, mastype(x)
é sempre<type 'instance'>
.Isso reflete o fato de que todas as instâncias de estilo antigo, independentemente de sua classe, são implementadas com um único tipo interno, chamado instância.
Classes de novo estilo foram introduzidas no Python 2.2 para unificar os conceitos de classe e tipo.Uma classe de novo estilo é simplesmente um tipo definido pelo usuário, nem mais, nem menos.
Se x é uma instância de uma classe de novo estilo, então
type(x)
é tipicamente o mesmo quex.__class__
(Embora isso não seja garantido-uma instância de classe de novo estilo tem permissão para substituir o valor retornado parax.__class__
).A principal motivação para a introdução de classes de novo estilo é fornecer um modelo de objeto unificado com um metamodelo completo.
Ele também possui vários benefícios imediatos, como a capacidade de subclasse a maioria dos tipos internos ou a introdução de "descritores", que permitem propriedades computadas.
Por motivos de compatibilidade, as classes ainda são antigas por padrão.
As aulas de novo estilo são criadas especificando outra classe de novo estilo (ou seja,um tipo) como uma classe pai ou o objeto "Tipo de nível superior", se nenhum outro pai for necessário.
O comportamento das classes de novo estilo difere do das classes de estilo antigo em vários detalhes importantes, além de que tipo retorna.
Algumas dessas mudanças são fundamentais para o novo modelo de objeto, como os métodos especiais são invocados.Outros são "correções" que não puderam ser implementadas antes para preocupações com compatibilidade, como a ordem de resolução do método em caso de herança múltipla.
Python 3 só tem classes de novo estilo.
Não importa se você subclassifica de
object
ou não, as aulas são de estilo novo no Python 3.
Outras dicas
Em termos de declaração:
As classes do novo estilo herdam do objeto ou de outra classe do novo estilo.
class NewStyleClass(object):
pass
class AnotherNewStyleClass(NewStyleClass):
pass
As aulas do estilo antigo, não.
class OldStyleClass():
pass
Mudanças importantes de comportamento entre classes de estilo antigo e novo
- super adicionado
- MRO alterado (explicado abaixo)
- descritores adicionado
- novos objetos de classe de estilo não podem ser gerados, a menos que sejam derivados de
Exception
(exemplo abaixo) __slots__
adicionado
MRO (Ordem de Resolução de Método) alterada
Foi mencionado em outras respostas, mas aqui vai um exemplo concreto da diferença entre o MRO clássico e o MRO C3 (usado nas classes do novo estilo).
A questão é a ordem em que os atributos (que incluem métodos e variáveis de membro) são procurados na herança múltipla.
Aulas clássicas faça uma primeira pesquisa em profundidade da esquerda para a direita.Pare na primeira partida.Eles não têm o __mro__
atributo.
class C: i = 0
class C1(C): pass
class C2(C): i = 2
class C12(C1, C2): pass
class C21(C2, C1): pass
assert C12().i == 0
assert C21().i == 2
try:
C12.__mro__
except AttributeError:
pass
else:
assert False
Aulas de novo estilo MRO é mais complicado de sintetizar em uma única frase em inglês.É explicado em detalhes aqui.Uma de suas propriedades é que uma classe Base só é pesquisada depois que todas as suas classes Derivadas tiverem sido pesquisadas.Eles têm o __mro__
atributo que mostra a ordem de pesquisa.
class C(object): i = 0
class C1(C): pass
class C2(C): i = 2
class C12(C1, C2): pass
class C21(C2, C1): pass
assert C12().i == 2
assert C21().i == 2
assert C12.__mro__ == (C12, C1, C2, C, object)
assert C21.__mro__ == (C21, C2, C1, C, object)
Objetos de classe de novo estilo não podem ser gerados, a menos que sejam derivados de Exception
Por volta do Python 2.5, muitas classes podiam ser criadas; por volta do Python 2.6, isso foi removido.No Python 2.7.3:
# OK, old:
class Old: pass
try:
raise Old()
except Old:
pass
else:
assert False
# TypeError, new not derived from `Exception`.
class New(object): pass
try:
raise New()
except TypeError:
pass
else:
assert False
# OK, derived from `Exception`.
class New(Exception): pass
try:
raise New()
except New:
pass
else:
assert False
# `'str'` is a new style object, so you can't raise it:
try:
raise 'str'
except TypeError:
pass
else:
assert False
As classes de estilo antigo ainda são um pouco mais rápidas para pesquisa de atributos.Isso geralmente não é importante, mas pode ser útil em código Python 2.x sensível ao desempenho:
In [3]: class A: ...: def __init__(self): ...: self.a = 'hi there' ...: In [4]: class B(object): ...: def __init__(self): ...: self.a = 'hi there' ...: In [6]: aobj = A() In [7]: bobj = B() In [8]: %timeit aobj.a 10000000 loops, best of 3: 78.7 ns per loop In [10]: %timeit bobj.a 10000000 loops, best of 3: 86.9 ns per loop
Guido escreveu A história interna das aulas do novo estilo, um artigo realmente ótimo sobre classes de estilo novo e antigo em Python.
Python 3 tem apenas classes de novo estilo, mesmo se você escrever uma 'classe de estilo antigo', ela é implicitamente derivada de object
.
As classes do novo estilo têm alguns recursos avançados que faltam nas classes do estilo antigo, como super
e o novo C3 mro, alguns métodos mágicos, etc.
Aqui está uma diferença muito prática entre Verdadeiro/Falso.A única diferença entre as duas versões do código a seguir é que na segunda versão Person herda do objeto.Fora isso, as duas versões são idênticas, mas com resultados diferentes:
1) aulas à moda antiga
class Person():
_names_cache = {}
def __init__(self,name):
self.name = name
def __new__(cls,name):
return cls._names_cache.setdefault(name,object.__new__(cls,name))
ahmed1 = Person("Ahmed")
ahmed2 = Person("Ahmed")
print ahmed1 is ahmed2
print ahmed1
print ahmed2
>>> False
<__main__.Person instance at 0xb74acf8c>
<__main__.Person instance at 0xb74ac6cc>
>>>
2) aulas de novo estilo
class Person(object):
_names_cache = {}
def __init__(self,name):
self.name = name
def __new__(cls,name):
return cls._names_cache.setdefault(name,object.__new__(cls,name))
ahmed1 = Person("Ahmed")
ahmed2 = Person("Ahmed")
print ahmed2 is ahmed1
print ahmed1
print ahmed2
>>> True
<__main__.Person object at 0xb74ac66c>
<__main__.Person object at 0xb74ac66c>
>>>
Classes de novo estilo herdam de object
e deve ser escrito como tal no Python 2.2 em diante (ou seja, class Classname(object):
em vez de class Classname:
).A principal mudança é unificar tipos e classes, e o bom efeito colateral disso é que permite herdar tipos integrados.
Ler descrição para mais detalhes.
Novas classes de estilo podem usar super(Foo, self)
onde Foo
é uma aula e self
é a instância.
super(type[, object-or-type])
Retorna um objeto proxy que delega chamadas de método a uma classe pai ou irmã do tipo.Isto é útil para acessar métodos herdados que foram substituídos em uma classe.A ordem de pesquisa é a mesma usada por getattr() exceto que o tipo em si é ignorado.
E no Python 3.x você pode simplesmente usar super()
dentro de uma classe sem parâmetros.
Ou melhor, você deve sempre usar classes de novo estilo, a menos que você tem código que precisa funcionar com versões do Python anteriores à 2.2.