
Uma nova seleção de dicas e programação em Python no meu feed @pythonetc.
← 
Coleções anterioresSe a instância da classe não tiver um atributo com o nome fornecido, ela tentará acessar o atributo da classe com o mesmo nome.
>>> class A: ... x = 2 ... >>> Ax 2 >>> A().x 2 
Uma instância pode facilmente ter um atributo que a classe não possui ou ter um atributo com um valor diferente:
 >>> class A: ... x = 2 ... def __init__(self): ... self.x = 3 ... self.y = 4 ... >>> A().x 3 >>> Ax 2 >>> A().y 4 >>> Ay AttributeError: type object 'A' has no attribute 'y' 
Se você deseja que a instância se comporte como se não tivesse um atributo, embora a classe o possua, será necessário criar um descritor personalizado que proíba o acesso a partir desta instância:
 class ClassOnlyDescriptor: def __init__(self, value): self._value = value self._name = None  
Veja também como o decorador do Django do 
classonlymethod : 
https://github.com/django/django/blob/b709d701303b3877387020c1558a590713b09853/django/utils/decorators.py#L6As funções declaradas no corpo da classe não estão acessíveis no escopo desta classe. Isso ocorre porque esse escopo existe apenas durante a criação da classe.
 >>> class A: ... x = 2 ... def f(): ... print(x) ... f() ... [...] NameError: name 'x' is not defined 
Isso geralmente não é um problema: os métodos são declarados dentro da classe apenas para se tornarem métodos e serem chamados posteriormente:
 >>> class A: ... x = 2 ... def f(self): ... print(self.x) ... >>> >>> >>> A().f() 2 
Surpreendentemente, o mesmo se aplica às compreensões. Eles têm seu próprio escopo e também não têm acesso ao escopo das classes. Isso é muito lógico em termos de compreensão do gerador: o código neles é executado quando a classe já está criada.
 >>> class A: ... x = 2 ... y = [x for _ in range(5)] ... [...] NameError: name 'x' is not defined 
No entanto, as compreensões não têm acesso ao 
self . A única maneira de fornecer acesso ao 
x é adicionar outro escopo (sim, solução estúpida):
 >>> class A: ... x = 2 ... y = (lambda x=x: [x for _ in range(5)])() ... >>> Ay [2, 2, 2, 2, 2] 
No Python, 
None equivalente a 
None , então pode parecer que você pode verificar 
None com 
== :
 ES_TAILS = ('s', 'x', 'z', 'ch', 'sh') def make_plural(word, exceptions=None): if exceptions == None:  
Mas isso será um erro. Sim, 
None é igual a 
None , mas não é só isso. Objetos personalizados também podem ser 
None :
 >>> class A: ... def __eq__(self, other): ... return True ... >>> A() == None True >>> A() is None False 
A única maneira correta de comparar com 
None é usar 
is None .
Os números de ponto flutuante no Python podem ter valores de NaN. Por exemplo, esse número pode ser obtido usando 
math.nan . 
nan não 
nan igual a nada, inclusive a si próprio:
 >>> math.nan == math.nan False 
Além disso, um objeto NaN não é exclusivo; você pode ter vários objetos NaN diferentes de fontes diferentes:
 >>> float('nan') nan >>> float('nan') is float('nan') False 
Isso significa que, em geral, você não pode usar o NaN como uma chave de dicionário:
 >>> d = {} >>> d[float('nan')] = 1 >>> d[float('nan')] = 2 >>> d {nan: 1, nan: 2} 
typing permite definir tipos para geradores. Além disso, você pode especificar qual tipo é gerado, que é passado para o gerador e qual é retornado usando 
return . Por exemplo, 
Generator[int, None, bool] gera números inteiros, retorna Booleans e não suporta 
g.send() .
Mas o exemplo é mais complicado. 
chain_while dados de outros geradores até que um deles retorne um valor que é um sinal de parada de acordo com a função de 
condition :
 from typing import Generator, Callable, Iterable, TypeVar Y = TypeVar('Y') S = TypeVar('S') R = TypeVar('R') def chain_while( iterables: Iterable[Generator[Y, S, R]], condition: Callable[[R], bool], ) -> Generator[Y, S, None]: for it in iterables: result = yield from it if not condition(result): break def r(x: int) -> Generator[int, None, bool]: yield from range(x) return x % 2 == 1 print(list(chain_while( [ r(5), r(4), r(3), ], lambda x: x is True, ))) 
Definir anotações para o método de fábrica não é tão fácil quanto parece. Só quero usar algo como isto:
 class A: @classmethod def create(cls) -> 'A': return cls() 
Mas vai estar errado. O truque é que 
create retorna não 
A , retorna 
cls , que é 
A ou um de seus descendentes. Dê uma olhada no código:
 class A: @classmethod def create(cls) -> 'A': return cls() class B(A): @classmethod def create(cls) -> 'B': return super().create() 
O resultado da verificação mypy é o erro de 
error: Incompatible return value type (got "A", expected "B") . Mais uma vez, o problema é que 
super().create() anotado como retornando 
A , embora, neste caso, retorne 
BIsso pode ser corrigido se estiver anotando 
cls usando 
TypeVar :
 AType = TypeVar('AType') BType = TypeVar('BType') class A: @classmethod def create(cls: Type[AType]) -> AType: return cls() class B(A): @classmethod def create(cls: Type[BType]) -> BType: return super().create() 
create agora retorna uma instância da classe 
cls . No entanto, essas anotações são muito vagas, perdemos informações de que 
cls é um subtipo de 
A :
 AType = TypeVar('AType') class A: DATA = 42 @classmethod def create(cls: Type[AType]) -> AType: print(cls.DATA) return cls() 
O erro 
"Type[AType]" has no attribute "DATA" .
Para corrigi-lo, você deve definir explicitamente 
AType como um subtipo de 
A Para isso, 
TypeVar com o argumento 
bound é usado.
 AType = TypeVar('AType', bound='A') BType = TypeVar('BType', bound='B') class A: DATA = 42 @classmethod def create(cls: Type[AType]) -> AType: print(cls.DATA) return cls() class B(A): @classmethod def create(cls: Type[BType]) -> BType: return super().create()