Qual é a diferença entre o estilo antigo e as novas classes de estilo no Python? Quando devo usar um ou outro?
Qual é a diferença entre o estilo antigo e as novas classes de estilo no Python? Quando devo usar um ou outro?
Respostas:
Das classes clássica e de novo estilo :
Até o Python 2.1, as classes de estilo antigo eram o único sabor disponível para o usuário.
O conceito de classe (de estilo antigo) não tem relação com o conceito de tipo: se
x
é uma instância de uma classe de estilo antigo,x.__class__
designa a classe dex
, mastype(x)
é sempre<type 'instance'>
.Isso reflete o fato de que todas as instâncias do estilo antigo, independentemente de sua classe, são implementadas com um único tipo interno, chamado instância.
As 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,
type(x)
normalmente é igual ax.__class__
(embora isso não seja garantido - é permitido que uma instância de classe de novo estilo substitua o valor retornadox.__class__
).A principal motivação para a introdução de novas classes de estilo é fornecer um modelo de objeto unificado com um metamodelo completo .
Ele também possui vários benefícios imediatos, como a capacidade de subclassificar a maioria dos tipos internos ou a introdução de "descritores", que habilitam propriedades calculadas.
Por motivos de compatibilidade, as classes ainda são antigas por padrão .
As classes 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 estilo novo difere do das classes de estilo antigo em vários detalhes importantes, além do tipo de retorno.
Algumas dessas mudanças são fundamentais para o novo modelo de objeto, como a maneira como métodos especiais são chamados. Outras são "correções" que antes não podiam ser implementadas por questões de compatibilidade, como a ordem de resolução do método em caso de herança múltipla.
O Python 3 possui apenas classes de novo estilo .
Não importa se você subclassifica
object
ou não, as classes são de novo estilo no Python 3.
super()
não funcionam em aulas de estilo antigo. Sem mencionar, como diz o artigo, existem correções fundamentais, como MRO, e métodos especiais, que são mais do que uma boa razão para usá-lo.
Em termos de declaração:
As classes de novo estilo herdam do objeto ou de outra classe de novo estilo.
class NewStyleClass(object):
pass
class AnotherNewStyleClass(NewStyleClass):
pass
As aulas de estilo antigo não.
class OldStyleClass():
pass
Nota do Python 3:
O Python 3 não suporta classes de estilo antigo, portanto, qualquer um dos formulários mencionados acima resulta em uma classe de novo estilo.
object
.
class AnotherOldStyleClass: pass
class A: pass
e class A(): pass
são estritamente equivalentes. O primeiro significa "A não herda nenhuma classe pai" e o segundo significa "A não herda nenhuma classe pai" . Isso é bem parecido com not is
eis not
Alterações importantes de comportamento entre classes de estilo antigas e novas
Exception
(exemplo abaixo)__slots__
adicionadoFoi mencionado em outras respostas, mas aqui vai um exemplo concreto da diferença entre MRO clássico e C3 MRO (usado em novas classes de estilo).
A questão é a ordem na qual os atributos (que incluem métodos e variáveis-membro) são pesquisados em herança múltipla.
As aulas clássicas fazem uma pesquisa profunda 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
As classes de novo estilo MRO são mais complicadas de sintetizar em uma única frase em inglês. É explicado em detalhes aqui . Uma de suas propriedades é que uma classe base é pesquisada apenas uma vez que todas as suas classes derivadas foram. 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)
Exception
Em torno do Python 2.5 muitas classes poderiam ser geradas e, em torno 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 marginalmente mais rápidas para a pesquisa de atributos. Isso geralmente não é importante, mas pode ser útil no código Python 2.x sensível ao desempenho:
Em [3]: classe A: ...: def __init __ (próprio): ...: self.a = 'oi' ...: Em [4]: classe B (objeto): ...: def __init __ (próprio): ...: self.a = 'oi' ...: Em [6]: aobj = A () Em [7]: bobj = B () Em [8]:% timeit aobj.a 10000000 loops, o melhor de 3: 78,7 ns por loop Em [10]:% timeit bobj.a 10000000 loops, o melhor de 3: 86,9 ns por loop
%timeit aobj.a
10000000 loops, best of 3: 66.1 ns per loop
%timeit bobj.a
10000000 loops, best of 3: 53.9 ns per loop
Guido escreveu The Inside Story on New Style Classes , um excelente artigo sobre classes de estilo novo e antigo em Python.
O Python 3 possui apenas classe de novo estilo. Mesmo se você escrever uma 'classe de estilo antigo', ela é derivada implicitamente object
.
As classes de estilo novo têm alguns recursos avançados ausentes nas classes de estilo antigo, como super
o novo C3 mro , alguns métodos mágicos etc.
Aqui está uma diferença muito prática, verdadeira / falsa. A única diferença entre as duas versões do código a seguir é que, na segunda versão, Person herda de objeto . Fora isso, as duas versões são idênticas, mas com resultados diferentes:
Classes à 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>
>>>
Classes 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>
>>>
_names_cache
é um dicionário que armazena em cache (armazena para recuperação futura) todos os nomes para os quais você passa Person.__new__
. O método setdefault (definido em qualquer dicionário) usa dois argumentos: uma chave e um valor. Se a chave estiver no ditado, ela retornará seu valor. Se não estiver no dict, ele o definirá primeiro como o valor passado como segundo argumento e depois o retornará.
__new__()
é sempre chamado, e sempre constrói um novo objeto, e depois o lança. Nesse caso, a if
é preferível .setdefault()
.
__new__
na verdade não é uma coisa para classes de estilo antigo, não é usado na construção de instância (é apenas um nome aleatório que parece especial, como definição __spam__
). Portanto, a construção da classe de estilo antigo apenas chama __init__
, enquanto a construção de estilo novo chama __new__
(coalescendo para instância singleton por nome) para construir e __init__
inicializá-la.
As classes de novo estilo herdam object
e devem ser escritas como tal no Python 2.2 em diante (ou seja, em class Classname(object):
vez de class Classname:
). A principal mudança é unificar tipos e classes, e o bom efeito colateral disso é que ele permite herdar dos tipos internos.
Leia a descrição para mais detalhes.
Novas classes de estilo podem usar super(Foo, self)
where Foo
é uma classe e self
é a instância.
super(type[, object-or-type])
Retorne um objeto proxy que delega chamadas de método a uma classe pai ou irmã do tipo. Isso é útil para acessar métodos herdados que foram substituídos em uma classe. A ordem de pesquisa é igual à usada por getattr (), exceto que o próprio tipo é ignorado.
E no Python 3.x você pode simplesmente usar super()
dentro de uma classe sem nenhum parâmetro.
type(x)
. Se eu não estou subclassificando um tipo incorporado, não parece haver nenhuma vantagem que eu possa ver nas classes de novo estilo. Há uma desvantagem, que é a digitação extra de(object)
.