Pergunta

Você pode adicionar novas declarações (como print, raise, with) a sintaxe do Python?

Say, para permitir ..

mystatement "Something"

Ou

new_if True:
    print "example"

Não tanto se você deve , mas sim se é possível (curto de modificar o código de intérpretes python)

Foi útil?

Solução

Você pode encontrar este útil - internos Python: adicionando uma nova declaração para Python , citado aqui:


Este artigo é uma tentativa de entender melhor como o front-end de Python funciona. Basta ler a documentação e código-fonte pode ser um pouco chato, então eu estou tomando um hands-on abordagem aqui:. Eu estou indo para adicionar uma declaração until para Python

Toda a codificação para este artigo foi feito contra o corte de ponta Py3k filial na Python Mercurial repositório espelho .

A declaração until

Alguns idiomas, como Ruby, tem uma declaração until, que é o complemento de while (until num == 0 é equivalente a while num != 0). Em Ruby, eu posso escrever:

num = 3
until num == 0 do
  puts num
  num -= 1
end

E ele irá imprimir:

3
2
1

Então, eu quero adicionar uma capacidade semelhante para Python. Ou seja, ser capaz de escrever:

num = 3
until num == 0:
  print(num)
  num -= 1

A linguagem de defesa digressão

Este artigo não tenta sugerir a adição de uma declaração until para Python. Embora eu acho que essa declaração um faria algum código mais claro, e este artigo mostra como é fácil adicionar, eu respeito completamente a filosofia do minimalismo do Python. Tudo o que eu estou tentando fazer aqui, realmente, é ganhar alguns insights sobre o funcionamento interno do Python.

Modificando a gramática

Python usa um gerador de analisador personalizado nomeado pgen. Esta é uma LL (1) parser que converte Python código-fonte em uma árvore de análise. A entrada para o gerador de analisador é o Grammar/Grammar arquivo [1] . Este é um arquivo de texto simples que especifica a gramática do Python.

[1] : A partir daqui, as referências a arquivos na origem Python são dadas relativamente à raiz da árvore de origem, que é o diretório onde você executar configure e fazer para construir Python.

Duas modificações têm de ser feitas para o arquivo de gramática. A primeira é a de adicionar uma definição para a declaração until. I encontrada em que a instrução while foi definida (while_stmt), e until_stmt abaixo adicionado [2] :

compound_stmt: if_stmt | while_stmt | until_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated
if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
while_stmt: 'while' test ':' suite ['else' ':' suite]
until_stmt: 'until' test ':' suite

[2] : Isso demonstra uma técnica comum que eu uso quando modificar o código fonte que eu não estou familiarizado com: trabalho por semelhança . Este princípio não vai resolver todos os seus problemas, mas pode definitivamente facilitar o processo. Uma vez que tudo que tem que ser feito para while também tem que ser feito para until, ele serve como uma boa orientação bonita.

Note que eu já decidiu excluir a cláusula else de minha definição de until, apenas para torná-lo um pouco diferente (e porque, francamente eu não gosto da cláusula else de loops e não acho que ele se encaixa bem com o Zen de Python).

A segunda mudança é a de modificar a regra para compound_stmt para incluir until_stmt, como você pode ver no trecho acima. É logo após while_stmt, novamente.

Quando você executa make após modificar Grammar/Grammar, aviso que o programa pgen é executado para re-gerar Include/graminit.h e Python/graminit.c, e, em seguida, vários arquivos são re-compilado.

Modificar o código de geração AST

Depois do analisador Python criou uma árvore de análise, esta árvore é convertido em um AST, desde ASTs são muito mais simples para trabalhar com em fases subsequentes do processo de compilação.

Então, vamos visita Parser/Python.asdl que define a estrutura de ASTs do Python e adicione um nó AST para o nosso novo generacdeclaração odicetagcode, novamente à direita abaixo do until:

| While(expr test, stmt* body, stmt* orelse)
| Until(expr test, stmt* body)

Se você agora executar while, aviso de que antes de compilar um monte de arquivos, make é executado para gerar o código C a partir do arquivo de definição de AST. Este (como Parser/asdl_c.py) é outro exemplo do código-fonte Python usando uma mini-linguagem (em outras palavras, uma DSL) para simplificar a programação. Observe também que, desde Grammar/Grammar é um script Python, este é um tipo de bootstrapping - para construir Python a partir do zero, Python já tem que estar disponível.

Enquanto Parser/asdl_c.py gerado o código para gerir o nosso nó AST recém-definido (no Parser/asdl_c.py arquivos e Include/Python-ast.h), ainda temos que escrever o código que converte um nó parse-árvore relevante para ele com a mão. Isto é feito na Python/Python-ast.c arquivo. Lá, uma função chamada convertidos Python/ast.c de análise nós da árvore para as demonstrações em nós AST. Mais uma vez, guiados por nosso velho amigo ast_for_stmt, nós ir direto para a grande while para lidar com instruções compostas e adicionar uma cláusula de switch:

case while_stmt:
    return ast_for_while_stmt(c, ch);
case until_stmt:
    return ast_for_until_stmt(c, ch);

Agora, devemos implementar until_stmt. Aqui está:

static stmt_ty
ast_for_until_stmt(struct compiling *c, const node *n)
{
    /* until_stmt: 'until' test ':' suite */
    REQ(n, until_stmt);

    if (NCH(n) == 4) {
        expr_ty expression;
        asdl_seq *suite_seq;

        expression = ast_for_expr(c, CHILD(n, 1));
        if (!expression)
            return NULL;
        suite_seq = ast_for_suite(c, CHILD(n, 3));
        if (!suite_seq)
            return NULL;
        return Until(expression, suite_seq, LINENO(n), n->n_col_offset, c->c_arena);
    }

    PyErr_Format(PyExc_SystemError,
                 "wrong number of tokens for 'until' statement: %d",
                 NCH(n));
    return NULL;
}

Mais uma vez, este foi codificado enquanto perto olhando para o ast_for_until_stmt equivalente, com a diferença de que, para ast_for_while_stmt eu decidi não apoiar a cláusula until. Como esperado, a AST é criado de forma recursiva, usando outras funções AST criando assim else para a expressão condição e ast_for_expr para o corpo da declaração ast_for_suite. Finalmente, um novo nó chamado until é retornado.

Note que acessar o parse-árvore de nós Until usando alguns macros como n e NCH. Estes são dignos de entendimento - do código está em CHILD

.

Digressão: composição AST

Eu escolhi para criar um novo tipo de AST para a instrução Include/node.h, mas na verdade isso não é necessário. Eu poderia ter salvo algum trabalho e implementada a nova funcionalidade usando composição de nós AST existentes, uma vez que:

until condition:
   # do stuff

É funcionalmente equivalente a:

while not condition:
  # do stuff

Em vez de criar o nó until em Until, eu poderia ter criado um nó ast_for_until_stmt com um nó Not como uma criança. Desde o compilador AST já sabe como lidar com esses nós, os próximos passos do processo pode ser ignorado.

Compilando ASTs em bytecode

O próximo passo é compilar o AST em Python bytecode. A compilação tem um resultado intermediário que é um CFG (Controle de Fluxo Graph), mas desde que os mesmos alças de código que vou ignorar esse detalhe para agora e deixá-lo para outro artigo.

O código veremos a seguir é While. Seguindo o exemplo de Python/compile.c, encontramos a while função, que é responsável por compilar as declarações em bytecode. Nós adicionamos uma cláusula de compiler_visit_stmt:

case While_kind:
    return compiler_while(c, s);
case Until_kind:
    return compiler_until(c, s);

Se você quer saber o que Until é, é uma constante (na verdade um valor da enumeração Until_kind) gerado automaticamente a partir do arquivo de definição de AST em _stmt_kind. De qualquer forma, nós chamamos Include/Python-ast.h que, é claro, ainda não existe. Eu vou chegar a ele uma um momento.

Se você está curioso como eu, você vai perceber que compiler_until é peculiar. Nenhuma quantidade de compiler_visit_stmt-ping a árvore fonte revela onde é chamado. Quando este for o caso, apenas uma opção permanece - C macro-fu. Na verdade, a uma curta investigação nos leva a macro grep definidos no VISIT:

#define VISIT(C, TYPE, V) {\
    if (!compiler_visit_ ## TYPE((C), (V))) \
        return 0; \

É usado para invocar Python/compile.c em compiler_visit_stmt. Voltar para o nosso negócio, no entanto ...

Como prometido, está aqui compiler_body:

static int
compiler_until(struct compiler *c, stmt_ty s)
{
    basicblock *loop, *end, *anchor = NULL;
    int constant = expr_constant(s->v.Until.test);

    if (constant == 1) {
        return 1;
    }
    loop = compiler_new_block(c);
    end = compiler_new_block(c);
    if (constant == -1) {
        anchor = compiler_new_block(c);
        if (anchor == NULL)
            return 0;
    }
    if (loop == NULL || end == NULL)
        return 0;

    ADDOP_JREL(c, SETUP_LOOP, end);
    compiler_use_next_block(c, loop);
    if (!compiler_push_fblock(c, LOOP, loop))
        return 0;
    if (constant == -1) {
        VISIT(c, expr, s->v.Until.test);
        ADDOP_JABS(c, POP_JUMP_IF_TRUE, anchor);
    }
    VISIT_SEQ(c, stmt, s->v.Until.body);
    ADDOP_JABS(c, JUMP_ABSOLUTE, loop);

    if (constant == -1) {
        compiler_use_next_block(c, anchor);
        ADDOP(c, POP_BLOCK);
    }
    compiler_pop_fblock(c, LOOP, loop);
    compiler_use_next_block(c, end);

    return 1;
}

Eu tenho uma confissão a fazer: este código não foi escrito com base em uma profunda compreensão do Python bytecode. Como o resto do artigo, que foi feito em imitação da função parentes compiler_until. Ao lê-lo com cuidado, no entanto, manter em mente que o Python VM é baseada em pilha, e olhando para a documentação do módulo compiler_while, que tem uma lista de Python bytecodes com descrições, é possível compreender o que está acontecendo.

É isso aí, estamos a fazer ... não estamos?

Depois de fazer todas as alterações e executar dis, podemos executar o Python recém-compilados e tentar o nosso nova declaração make:

>>> until num == 0:
...   print(num)
...   num -= 1
...
3
2
1

Voila, ele funciona! Vamos ver o bytecode criado para a nova instrução usando o módulo until da seguinte forma:

import dis

def myfoo(num):
    until num == 0:
        print(num)
        num -= 1

dis.dis(myfoo)

Aqui está o resultado:

4           0 SETUP_LOOP              36 (to 39)
      >>    3 LOAD_FAST                0 (num)
            6 LOAD_CONST               1 (0)
            9 COMPARE_OP               2 (==)
           12 POP_JUMP_IF_TRUE        38

5          15 LOAD_NAME                0 (print)
           18 LOAD_FAST                0 (num)
           21 CALL_FUNCTION            1
           24 POP_TOP

6          25 LOAD_FAST                0 (num)
           28 LOAD_CONST               2 (1)
           31 INPLACE_SUBTRACT
           32 STORE_FAST               0 (num)
           35 JUMP_ABSOLUTE            3
      >>   38 POP_BLOCK
      >>   39 LOAD_CONST               0 (None)
           42 RETURN_VALUE

A operação mais interessante é o número 12: se a condição for verdadeira, nós saltar para depois do loop. Esta é semântica correta para dis. Se o salto não é executado, o corpo do laço continua funcionando até que ele salta de volta para a condição em operação 35.

Sentir-se bem sobre a minha mudança, então eu tentei executar o (until execução) em vez de mostrar o seu bytecode. O resultado foi menor do que animadores:

Traceback (most recent call last):
  File "zy.py", line 9, in
    myfoo(3)
  File "zy.py", line 5, in myfoo
    print(num)
SystemError: no locals when loading 'print'

Uau ... isso não pode ser bom. Então, o que deu errado?

O caso da tabela de símbolos faltando

Uma das etapas executadas pelo compilador Python ao compilar o AST é criar uma tabela de símbolos para o código que compila. A chamada para myfoo(3) em PySymtable_Build chama o módulo da tabela de símbolos (PyAST_Compile), que anda a AST de uma maneira semelhante às funções de geração de código. Ter uma tabela de símbolos para cada escopo ajuda a figura compilador para fora algumas informações-chave, tais como quais as variáveis ??são globais e que são locais para um escopo.

Para corrigir o problema, temos que modificar a função Python/symtable.c em symtable_visit_stmt, adicionando o código para lidar com declarações Python/symtable.c, após o código semelhante para as demonstrações until [3] :

case While_kind:
    VISIT(st, expr, s->v.While.test);
    VISIT_SEQ(st, stmt, s->v.While.body);
    if (s->v.While.orelse)
        VISIT_SEQ(st, stmt, s->v.While.orelse);
    break;
case Until_kind:
    VISIT(st, expr, s->v.Until.test);
    VISIT_SEQ(st, stmt, s->v.Until.body);
    break;

[3] : By the way, sem este código há um aviso do compilador para while. O compilador percebe que o valor Python/symtable.c enumeração não é tratado na instrução switch de Until_kind e queixa-se. É sempre importante verificar se há avisos do compilador!

E agora nós realmente são feitos. Compilando a fonte após esta mudança faz com que a execução do trabalho symtable_visit_stmt como esperado.

Conclusão

Neste artigo eu demonstrei como adicionar uma nova declaração para Python. Embora exigindo um pouco de mexer no código do compilador Python, a mudança não foi difícil de implementar, porque eu usei uma declaração semelhante e existente como uma diretriz.

O compilador Python é um pedaço sofisticado de software, e não tenho a pretensão de ser um especialista nisso. No entanto, eu estou realmente interessado nos internos do Python, e particularmente o seu front-end. Portanto, eu encontrei este exercício um companheiro muito útil para o estudo teórico dos princípios do compilador e código fonte. Ele servirá como base para futuros artigos que irão se aprofundar no compilador.

Referências

Eu usei algumas excelentes referências para a construção deste artigo. Aqui estão eles, em nenhuma ordem particular:

  • PEP 339: Projeto do CPython compilador - provavelmente o mais importante e peça abrangente de funcionário documentação para o compilador Python. Sermuito curto, dolorosamente mostra a escassez de boa documentação dos internos do Python.
  • "python compilador Internals" - um artigo de Thomas Lee
  • "Python: Desenho e Implementação" - uma apresentação por Guido van Rossum
  • Python (2.5) Virtual Machine, A visita guiada - uma apresentação por Peter Tröger

fonte original

Outras dicas

Uma maneira de fazer as coisas como esta é a pré-processar a fonte e modificá-lo, traduzindo o seu comunicado de python. Há vários problemas esta abordagem trará, e eu não recomendo para uso geral, mas para a experimentação com a linguagem, ou metaprogramming de propósito específico, ele pode ocasionalmente ser útil.

Por exemplo, digamos que queremos introduzir uma declaração "myPrint", que em vez de imprimir a tela em vez logs para um arquivo específico. ou seja:

myprint "This gets logged to file"

seria equivalente a

print >>open('/tmp/logfile.txt','a'), "This gets logged to file"

Existem várias opções sobre a forma de fazer a substituição, de substituição regex para gerar um AST, para escrever seu próprio analisador dependendo python quão perto sua sintaxe jogos existente. Uma boa abordagem intermediário é usar o módulo tokenizador. Isso deve permitir que você adicionar novas palavras-chave, estruturas de controle etc ao interpretar a fonte de forma semelhante ao interpretador Python, evitando assim a ruptura soluções regex bruto causaria. Pelo exposto "myPrint", você pode escrever o seguinte código de transformação:

import tokenize

LOGFILE = '/tmp/log.txt'
def translate(readline):
    for type, name,_,_,_ in tokenize.generate_tokens(readline):
        if type ==tokenize.NAME and name =='myprint':
            yield tokenize.NAME, 'print'
            yield tokenize.OP, '>>'
            yield tokenize.NAME, "open"
            yield tokenize.OP, "("
            yield tokenize.STRING, repr(LOGFILE)
            yield tokenize.OP, ","
            yield tokenize.STRING, "'a'"
            yield tokenize.OP, ")"
            yield tokenize.OP, ","
        else:
            yield type,name

(Isto faz myPrint efetivamente uma palavra-chave, por isso o uso como uma variável em outros lugares provavelmente causará problemas)

O problema, então, é como usá-lo para que seu código é utilizável a partir de python. Uma maneira seria apenas para escrever sua própria função de importação, e usá-lo para carregar código escrito em seu idioma personalizado. ou seja:

import new
def myimport(filename):
    mod = new.module(filename)
    f=open(filename)
    data = tokenize.untokenize(translate(f.readline))
    exec data in mod.__dict__
    return mod

Isso requer que você lidar com o seu código personalizado de forma diferente a partir de módulos normais python no entanto. ou seja, "some_mod = myimport("some_mod.py")" em vez de "import some_mod"

Outra solução bastante limpo (embora hacky) é criar uma codificação personalizado (veja PEP 263 ) como esta receita demonstra. Você poderia implementar isso como:

import codecs, cStringIO, encodings
from encodings import utf_8

class StreamReader(utf_8.StreamReader):
    def __init__(self, *args, **kwargs):
        codecs.StreamReader.__init__(self, *args, **kwargs)
        data = tokenize.untokenize(translate(self.stream.readline))
        self.stream = cStringIO.StringIO(data)

def search_function(s):
    if s!='mylang': return None
    utf8=encodings.search_function('utf8') # Assume utf8 encoding
    return codecs.CodecInfo(
        name='mylang',
        encode = utf8.encode,
        decode = utf8.decode,
        incrementalencoder=utf8.incrementalencoder,
        incrementaldecoder=utf8.incrementaldecoder,
        streamreader=StreamReader,
        streamwriter=utf8.streamwriter)

codecs.register(search_function)

Agora, após este código é executado (. Por exemplo, você poderia colocá-lo em sua .pythonrc ou site.py) qualquer código que começa com o comentário "codificação #: mylang" será automaticamente traduzido através do acima pré-processamento passo. por exemplo.

# coding: mylang
myprint "this gets logged to file"
for i in range(10):
    myprint "so does this : ", i, "times"
myprint ("works fine" "with arbitrary" + " syntax" 
  "and line continuations")

Advertências:

Existem problemas para a abordagem de pré-processamento, como você provavelmente vai estar familiarizado com se você já trabalhou com o pré-processador C. A principal delas é a depuração. Todos python vê é o arquivo pré-processado que significa que o texto impresso no rastreamento de pilha etc irá se referir a isso. Se você realizou a tradução significativa, isso pode ser muito diferente do seu texto de origem. O exemplo acima não muda os números de linha etc, então não vai ser muito diferente, mas quanto mais você alterá-la, o mais difícil será para descobrir.

Sim, até certo ponto, isso é possível. Há um módulo lá fora, que usos sys.settrace() para implementar goto e comefrom "palavras-chave":

from goto import goto, label
for i in range(1, 10):
  for j in range(1, 20):
    print i, j
    if j == 3:
      goto .end # breaking out from nested loop
label .end
print "Finished"

curta de mudar e recompilar o código fonte (que é possível com código aberto), a alteração do idioma base não é realmente possível.

Mesmo se você recompilar a fonte, que não seria python, apenas a sua versão alterada hackeado-up que você precisa ter muito cuidado para não introduzir erros em.

No entanto, eu não sei por que você iria querer. recursos orientados a objetos do Python torna bastante simples de obter resultados semelhantes com a linguagem tal como está.

resposta geral: você precisa para pré-processar seus arquivos de origem.

Mais resposta específica: instalar EasyExtend , e passar por etapas a seguir

i) Criar um novo Langlet (linguagem de extensão)

import EasyExtend
EasyExtend.new_langlet("mystmts", prompt = "my> ", source_ext = "mypy")

Sem especificação adicional um monte de arquivos será criado sob EasyExtend / langlets / mystmts /.

ii) Abrir mystmts / parsedef / Grammar.ext e adicionar as seguintes linhas

small_stmt: (expr_stmt | print_stmt  | del_stmt | pass_stmt | flow_stmt |
             import_stmt | global_stmt | exec_stmt | assert_stmt | my_stmt )

my_stmt: 'mystatement' expr

Isto é suficiente para definir a sintaxe de sua nova declaração. O small_stmt não-terminal é parte da gramática Python e é o lugar onde a nova declaração é viciado em. O analisador irá agora reconhecer a nova instrução ou seja, um arquivo de origem contendo será analisado. O compilador irá rejeitá-la, porque embora ele ainda tem que ser transformado em Python válido.

iii) Agora é preciso adicionar semântica da declaração. Para isso é preciso editar msytmts / langlet.py e adicionar um my_stmt nó visitante.

 def call_my_stmt(expression):
     "defines behaviour for my_stmt"
     print "my stmt called with", expression

 class LangletTransformer(Transformer):
       @transform
       def my_stmt(self, node):
           _expr = find_node(node, symbol.expr)
           return any_stmt(CST_CallFunc("call_my_stmt", [_expr]))

 __publish__ = ["call_my_stmt"]

iv) cd para langlets / mystmts e tipo

python run_mystmts.py

Agora, uma sessão deve ser iniciado e a declaração recém-definido pode ser usado:

__________________________________________________________________________________

 mystmts

 On Python 2.5.1 (r251:54863, Apr 18 2007, 08:51:08) [MSC v.1310 32 bit (Intel)]
 __________________________________________________________________________________

 my> mystatement 40+2
 my stmt called with 42

Muito poucos passos para chegar a uma declaração trivial, certo? Não há uma API ainda que permite que se define coisas simples, sem ter de se preocupar com gramáticas. Mas EE é muito confiável modulo alguns bugs. Então é só uma questão de tempo que uma API emerge que permite que os programadores definem coisas conveniente como operadores infixas ou declarações pequenos usando programação OO apenas conveniente. Para coisas mais complexas, como a incorporação de idiomas inteiros em Python por meio da construção de uma Langlet não há nenhuma maneira de ir em torno de uma abordagem gramatical completo.

Aqui está uma maneira muito simples, mas de baixa qualidade para adicionar novas declarações, no modo interpretativa única . Eu estou usando-o para os pequenos 1 letras comandos para editar anotações de genes utilizando apenas sys.displayhook, mas apenas para que eu pudesse responder a esta pergunta eu adicionei sys.excepthook para os erros de sintaxe também. O último é realmente feio, buscando o código cru do tampão readline. A vantagem é que é tão fácil de adicionar novas declarações desta forma.


jcomeau@intrepid:~/$ cat demo.py; ./demo.py
#!/usr/bin/python -i
'load everything needed under "package", such as package.common.normalize()'
import os, sys, readline, traceback
if __name__ == '__main__':
    class t:
        @staticmethod
        def localfunction(*args):
            print 'this is a test'
            if args:
                print 'ignoring %s' % repr(args)

    def displayhook(whatever):
        if hasattr(whatever, 'localfunction'):
            return whatever.localfunction()
        else:
            print whatever

    def excepthook(exctype, value, tb):
        if exctype is SyntaxError:
            index = readline.get_current_history_length()
            item = readline.get_history_item(index)
            command = item.split()
            print 'command:', command
            if len(command[0]) == 1:
                try:
                    eval(command[0]).localfunction(*command[1:])
                except:
                    traceback.print_exception(exctype, value, tb)
        else:
            traceback.print_exception(exctype, value, tb)

    sys.displayhook = displayhook
    sys.excepthook = excepthook
>>> t
this is a test
>>> t t
command: ['t', 't']
this is a test
ignoring ('t',)
>>> ^D

Eu encontrei um guia sobre a adição de novas informações:

https://troeger.eu/files/teaching/pythonvm08lab.pdf

Basicamente, para adicionar novas instruções, você deve editar Python/ast.c (entre outras coisas) e recompilar o binário python.

Embora seja possível, não. Você pode conseguir quase tudo através de funções e classes (que não vai exigir que as pessoas python recompilação apenas para executar o script ..)

É possível fazer isso usando EasyExtend :

EasyExtend (EE) é um pré-processador gerador e metaprogramming framework escrito em puro Python e integrado com CPython. O principal propósito de EasyExtend é a criação de linguagens de extensão adicionando ou seja, sintaxe e semântica personalizada para Python.

Não sem modificar o intérprete. Eu conheço um monte de línguas nos últimos anos têm sido descritos como "extensível", mas não da maneira que você está descrevendo. Você estender Python adicionando funções e classes.

Há uma linguagem baseado em python chamado Logix com que você pode fazer essas coisas. Não tem sido em desenvolvimento há algum tempo, mas as características que você pediu fazer o trabalho com a versão mais recente.

Algumas coisas podem ser feitas com decoradores. Vamos por exemplo assumir, Python não tinha comunicado with. Poderíamos, então, implementar um comportamento semelhante como este:

# ====== Implementation of "mywith" decorator ======

def mywith(stream):
    def decorator(function):
        try: function(stream)
        finally: stream.close()
    return decorator

# ====== Using the decorator ======

@mywith(open("test.py","r"))
def _(infile):
    for l in infile.readlines():
        print(">>", l.rstrip())

É uma solução bastante impura, porém, como feito aqui. Especialmente o comportamento onde o decorador chama a função e conjuntos _ para None é inesperado. Para esclarecimento: Este decorador é equivalente à escrita

def _(infile): ...
_ = mywith(open(...))(_) # mywith returns None.

e decoradores são normalmente esperado para modificar, não executar, funções.

Eu usei esse método antes em um script onde eu tive que definir temporariamente o diretório de trabalho para várias funções.

Não é adicionando exatamente novas declarações para a sintaxe da linguagem, mas macros são uma ferramenta poderosa: https: // github. com / lihaoyi / macropy

anos Dez atrás você não podia, e eu duvido que mudou. No entanto, não foi tão difícil para modificar a sintaxe de volta, então se você estivesse preparado para python recompilação, e duvido que mudou, também.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top