Recursos ocultos do Python





Troca de valor no local

>>> a = 10
>>> b = 5
>>> a, b
(10, 5)

>>> a, b = b, a
>>> a, b
(5, 10)

O lado direito da atribuição é uma expressão que cria uma nova tupla. O lado esquerdo da tarefa descomprime imediatamente essa tupla (não referenciada) para os nomes a e b .

Após a atribuição, a nova tupla é não referenciada e marcada para coleta de lixo, e os valores ligados a b foram trocados.

Conforme observado na seção tutorial do Python sobre estruturas de dados ,

Observe que a atribuição múltipla é realmente apenas uma combinação de empacotamento de tupla e descompactação de seqüência.


Tenha cuidado com argumentos padrão mutáveis

>>> def foo(x=[]):
...     x.append(1)
...     print x
... 
>>> foo()
[1]
>>> foo()
[1, 1]
>>> foo()
[1, 1, 1]

Em vez disso, você deve usar um valor de sentinela denotando "não fornecido" e substituir pelo mutável que deseja como padrão:

>>> def foo(x=None):
...     if x is None:
...         x = []
...     x.append(1)
...     print x
>>> foo()
[1]
>>> foo()
[1]

Criando objetos geradores

Se você escreve

x=(n for n in foo if bar(n))

você pode sair do gerador e atribuí-lo ao x. Agora isso significa que você pode fazer

for n in x:

A vantagem disso é que você não precisa de armazenamento intermediário, o que seria necessário se

x = [n for n in foo if bar(n)]

Em alguns casos, isso pode levar a uma aceleração significativa.

Você pode acrescentar muitas instruções if ao final do gerador, basicamente replicando loops aninhados:

>>> n = ((a,b) for a in range(0,2) for b in range(4,6))
>>> for i in n:
...   print i 

(0, 4)
(0, 5)
(1, 4)
(1, 5)

Gerentes de Contexto e a Declaração " with "

Introduzido no PEP 343 , um gerenciador de contexto é um objeto que age como um contexto de tempo de execução para um conjunto de instruções.

Como o recurso faz uso de novas palavras-chave, ele é introduzido gradualmente: ele está disponível no Python 2.5 por meio da diretiva __future__ . O Python 2.6 e superior (incluindo o Python 3) está disponível por padrão.

Eu usei muito a instrução "with" porque acho que é uma construção muito útil, aqui está uma demonstração rápida:

from __future__ import with_statement

with open('foo.txt', 'w') as f:
    f.write('hello!')

O que está acontecendo aqui nos bastidores é que a instrução "with" chama os métodos especiais __enter__ e __exit__ no objeto file. Os detalhes da exceção também são passados ​​para __exit__ se qualquer exceção foi levantada do corpo da instrução with, permitindo que o tratamento de exceções aconteça lá.

O que isso faz para você neste caso em particular é que ele garante que o arquivo seja fechado quando a execução ficar fora do escopo do pacote with , independentemente se isso ocorrer normalmente ou se uma exceção foi lançada. É basicamente uma maneira de abstrair o código comum de manipulação de exceção.

Outros casos de uso comuns para isso incluem o bloqueio com encadeamentos e transações do banco de dados.


Operadores de comparação de encadeamento:

>>> x = 5
>>> 1 < x < 10
True
>>> 10 < x < 20 
False
>>> x < 10 < x*10 < 100
True
>>> 10 > x <= 9
True
>>> 5 == x > 4
True

Caso você esteja pensando que está fazendo 1 < x , que sai como True , e então comparando True < 10 , que também é True , então não, realmente não é o que acontece (veja o último exemplo). 1 < x and x < 10 e x < 10 and 10 < x * 10 and x*10 < 100 , mas com menos digitação e cada termo é avaliado apenas uma vez.



Doctest : documentação e testes unitários ao mesmo tempo.

Exemplo extraído da documentação do Python:

def factorial(n):
    """Return the factorial of n, an exact integer >= 0.

    If the result is small enough to fit in an int, return an int.
    Else return a long.

    >>> [factorial(n) for n in range(6)]
    [1, 1, 2, 6, 24, 120]
    >>> factorial(-1)
    Traceback (most recent call last):
        ...
    ValueError: n must be >= 0

    Factorials of floats are OK, but the float must be an exact integer:
    """

    import math
    if not n >= 0:
        raise ValueError("n must be >= 0")
    if math.floor(n) != n:
        raise ValueError("n must be exact integer")
    if n+1 == n:  # catch a value like 1e300
        raise OverflowError("n too large")
    result = 1
    factor = 2
    while factor <= n:
        result *= factor
        factor += 1
    return result

def _test():
    import doctest
    doctest.testmod()    

if __name__ == "__main__":
    _test()

Criando novos tipos de maneira totalmente dinâmica

>>> NewType = type("NewType", (object,), {"x": "hello"})
>>> n = NewType()
>>> n.x
"hello"

que é exatamente o mesmo que

>>> class NewType(object):
>>>     x = "hello"
>>> n = NewType()
>>> n.x
"hello"

Provavelmente não é a coisa mais útil, mas é bom saber.

Edit : nome fixo do novo tipo, deve ser NewType para ser exatamente a mesma coisa que com a declaração da class .

Editar : Ajustou o título para descrever com mais precisão o recurso.


Mensagens principais :)

import this
# btw look at this module's source :)

De-cyphered :

O Zen do Python, por Tim Peters

Linda é melhor que feia.
Explícito é melhor que implícito.
Simples é melhor que complexo.
Complexo é melhor que complicado.
Flat é melhor que aninhado.
Esparso é melhor que denso.
A legibilidade conta.
Casos especiais não são especiais o suficiente para quebrar as regras.
Embora a praticidade supere a pureza.
Erros nunca devem passar silenciosamente.
A menos que seja explicitamente silenciado.
Em face da ambiguidade, recuse a tentação de adivinhar. Deve haver um - e de preferência apenas um - caminho óbvio para fazê-lo.
Embora esse caminho possa não ser óbvio a princípio, a menos que você seja holandês.
Agora é melhor que nunca.
Embora nunca tenha sido muitas vezes é melhor do que direito agora.
Se a implementação é difícil de explicar, é uma má ideia.
Se a implementação é fácil de explicar, pode ser uma boa ideia.
Os namespaces são uma ótima idéia - vamos fazer mais desses!


Para adicionar mais módulos python (especialmente aqueles de terceiros), a maioria das pessoas parece usar variáveis ​​de ambiente PYTHONPATH ou adicionar links simbólicos ou diretórios em seus diretórios de pacotes de sites. Outra maneira é usar arquivos * .pth. Aqui está a explicação do documento oficial do python:

"A maneira mais conveniente de modificar o caminho de pesquisa do python é adicionar um arquivo de configuração de caminho a um diretório que já está no caminho do Python, geralmente no diretório ... / site-packages /. Os arquivos de configuração do caminho têm uma extensão .pth. , e cada linha deve conter um único caminho que será anexado ao sys.path. (Como os novos caminhos são anexados ao sys.path, os módulos nos diretórios adicionados não substituirão os módulos padrão. Isso significa que você não pode usar esse mecanismo para instalar versões fixas de módulos padrão.) "


Se você não gosta de usar espaço em branco para denotar escopos, é possível usar o estilo C {} emitindo:

from __future__ import braces

O argumento da etapa nos operadores de fatia. Por exemplo:

a = [1,2,3,4,5]
>>> a[::2]  # iterate over the whole list in 2-increments
[1,3,5]

O caso especial x[::-1] é um idioma útil para 'x invertido'.

>>> a[::-1]
[5,4,3,2,1]

iter () pode ter um argumento que pode ser chamado

Por exemplo:

def seek_next_line(f):
    for c in iter(lambda: f.read(1),'\n'):
        pass

A função iter(callable, until_value) chama repetidamente callable e produz seu resultado até que until_value seja retornado.


Argumento de função descompactando

Você pode descompactar uma lista ou um dicionário como argumentos de função usando * e ** .

Por exemplo:

def draw_point(x, y):
    # do some magic

point_foo = (3, 4)
point_bar = {'y': 3, 'x': 2}

draw_point(*point_foo)
draw_point(**point_bar)

Atalho muito útil, pois as listas, tuplas e ditos são amplamente utilizados como contêineres.


Descritores

Eles são a mágica por trás de um monte de recursos principais do Python.

Quando você usa o acesso pontilhado para procurar um membro (por exemplo, xy), o Python primeiro procura o membro no dicionário da instância. Se não for encontrado, procurará no dicionário da turma. Se o encontrar no dicionário de classe e o objeto implementar o protocolo do descritor, em vez de apenas retorná-lo, o Python o executa. Um descritor é qualquer classe que implementa as __get__, __set__ou __delete__métodos.

Veja como você implementaria sua própria versão (somente leitura) da propriedade usando descritores:

class Property(object):
    def __init__(self, fget):
        self.fget = fget

    def __get__(self, obj, type):
        if obj is None:
            return self
        return self.fget(obj)

e você o usaria como a propriedade interna ():

class MyClass(object):
    @Property
    def foo(self):
        return "Foo!"

Os descritores são usados ​​no Python para implementar propriedades, métodos vinculados, métodos estáticos, métodos de classe e slots, entre outras coisas. Entendê-los torna mais fácil entender por que muitas coisas que anteriormente pareciam "peculiaridades" do Python são do jeito que são.

Raymond Hettinger tem um excelente tutorial que faz um trabalho muito melhor de descrevê-los do que eu.


Re-elevando exceções :

# Python 2 syntax
try:
    some_operation()
except SomeError, e:
    if is_fatal(e):
        raise
    handle_nonfatal(e)

# Python 3 syntax
try:
    some_operation()
except SomeError as e:
    if is_fatal(e):
        raise
    handle_nonfatal(e)

A instrução 'raise' sem argumentos dentro de um manipulador de erros diz ao Python para re-aumentar a exceção com o traceback original intacto , permitindo que você diga "oh, desculpe, desculpe, eu não quis pegar isso, desculpe, desculpe. "

Se você deseja imprimir, armazenar ou mexer com o traceback original, você pode obtê-lo com sys.exc_info () e imprimi-lo como o Python faria com o módulo 'traceback'.


Atribuição Condicional

x = 3 if (y == 1) else 2

Faz exatamente o que parece: "atribuir 3 a x se y é 1, caso contrário, atribuir 2 a x". Note que os parênteses não são necessários, mas eu gosto deles por legibilidade. Você também pode encadear se tiver algo mais complicado:

x = 3 if (y == 1) else 2 if (y == -1) else 1

Embora em um certo ponto, seja um pouco longe demais.

Note que você pode usar se ... else em qualquer expressão. Por exemplo:

(func1 if y == 1 else func2)(arg1, arg2) 

Aqui func1 será chamado se y for 1 e func2, caso contrário. Em ambos os casos, a função correspondente será chamada com argumentos arg1 e arg2.

Analogamente, o seguinte também é válido:

x = (class1 if y == 1 else class2)(arg1, arg2)

em que class1 e class2 são duas classes.


Os dicionários têm um método get ()

Os dicionários têm um método 'get ()'. Se você fizer d ['chave'] e a chave não estiver lá, você receberá uma exceção. Se você fizer d.get ('key'), você receberá None se 'key' não estiver lá. Você pode adicionar um segundo argumento para recuperar o item em vez de Nenhum, por exemplo: d.get ('key', 0).

É ótimo para coisas como somar números:

sum[value] = sum.get(value, 0) + 1


A sintaxe for ... else (consulte http://docs.python.org/ref/for.html )

for i in foo:
    if i == 0:
        break
else:
    print("i was never 0")

O bloco "else" será executado normalmente no final do loop for, a menos que a quebra seja chamada.

O código acima pode ser emulado da seguinte maneira:

found = False
for i in foo:
    if i == 0:
        found = True
        break
if not found: 
    print("i was never 0")

Formatação nomeada

% -formatação leva um dicionário (também aplica% i /% s etc. validação).

>>> print "The %(foo)s is %(bar)i." % {'foo': 'answer', 'bar':42}
The answer is 42.

>>> foo, bar = 'question', 123

>>> print "The %(foo)s is %(bar)i." % locals()
The question is 123.

E como locals () também é um dicionário, você pode simplesmente passar isso como um dict e ter% -substitions de suas variáveis ​​locais. Eu acho que isso é desaprovado, mas simplifica as coisas ..

Nova formatação de estilo

>>> print("The {foo} is {bar}".format(foo='answer', bar=42))

Inclusões de lista aninhadas e expressões geradoras:

[(i,j) for i in range(3) for j in range(i) ]    
((i,j) for i in range(4) for j in range(i) )

Eles podem substituir grandes blocos de código de loop aninhado.


Cláusula exception else :

try:
  put_4000000000_volts_through_it(parrot)
except Voom:
  print "'E's pining!"
else:
  print "This parrot is no more!"
finally:
  end_sketch()

O uso da cláusula else é melhor do que adicionar código adicional à cláusula try porque evita a captura acidental de uma exceção que não foi levantada pelo código protegido pela instrução try ... except.

Veja http://docs.python.org/tut/node10.html


O ROT13 é uma codificação válida para o código-fonte, quando você usa a declaração de codificação correta na parte superior do arquivo de código:

#!/usr/bin/env python
# -*- coding: rot13 -*-

cevag "Uryyb fgnpxbiresybj!".rapbqr("rot13")

Expressões regulares legíveis

No Python, você pode dividir uma expressão regular em várias linhas, nomear suas correspondências e inserir comentários.

Exemplo de sintaxe detalhada (de Dive into Python ):

>>> pattern = """
... ^                   # beginning of string
... M{0,4}              # thousands - 0 to 4 M's
... (CM|CD|D?C{0,3})    # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 C's),
...                     #            or 500-800 (D, followed by 0 to 3 C's)
... (XC|XL|L?X{0,3})    # tens - 90 (XC), 40 (XL), 0-30 (0 to 3 X's),
...                     #        or 50-80 (L, followed by 0 to 3 X's)
... (IX|IV|V?I{0,3})    # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 I's),
...                     #        or 5-8 (V, followed by 0 to 3 I's)
... $                   # end of string
... """
>>> re.search(pattern, 'M', re.VERBOSE)

Exemplo de correspondência de nomes (do HOWTO de expressão regular )

>>> p = re.compile(r'(?P<word>\b\w+\b)')
>>> m = p.search( '(((( Lots of punctuation )))' )
>>> m.group('word')
'Lots'

Você também pode escrever um regex com mais detalhes sem usar re.VERBOSE graças à concatenação literal de string.

>>> pattern = (
...     "^"                 # beginning of string
...     "M{0,4}"            # thousands - 0 to 4 M's
...     "(CM|CD|D?C{0,3})"  # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 C's),
...                         #            or 500-800 (D, followed by 0 to 3 C's)
...     "(XC|XL|L?X{0,3})"  # tens - 90 (XC), 40 (XL), 0-30 (0 to 3 X's),
...                         #        or 50-80 (L, followed by 0 to 3 X's)
...     "(IX|IV|V?I{0,3})"  # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 I's),
...                         #        or 5-8 (V, followed by 0 to 3 I's)
...     "$"                 # end of string
... )
>>> print pattern
"^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$"






hidden-features