Python __setattr__ e __getAtTr__ para escopo global?
-
27-09-2019 - |
Pergunta
Suponha que eu precise criar meu próprio pequeno DSL que usaria o Python para descrever uma determinada estrutura de dados. Por exemplo, eu gostaria de poder escrever algo como
f(x) = some_stuff(a,b,c)
e tenha python, em vez de reclamar de identificadores não declarados ou tentar invocar a função de alguma forma, convertê -la em uma expressão literal para minha conveniência adicional.
É possível obter uma aproximação razoável a isso criando uma classe com redefinido adequadamente __getattr__
e __setattr__
Métodos e use -o da seguinte forma:
e = Expression()
e.f[e.x] = e.some_stuff(e.a, e.b, e.c)
Seria legal, porém, se fosse possível se livrar do irritante "E". prefixos e talvez até evite o uso de []. Então, eu queria saber: é possível de alguma forma "redefinir temporariamente" as pesquisas e tarefas globais de nome? Em uma nota relacionada, talvez haja bons pacotes para alcançar facilmente essa funcionalidade "citando" para expressões de Python?
Solução
Não tenho certeza se é uma boa ideia, mas pensei em De uma chance. Para resumir:
class PermissiveDict(dict):
default = None
def __getitem__(self, item):
try:
return dict.__getitem__(self, item)
except KeyError:
return self.default
def exec_with_default(code, default=None):
ns = PermissiveDict()
ns.default = default
exec code in ns
return ns
Outras dicas
Você pode querer dar uma olhada no ast
ou parser
Os módulos incluídos no python para analisar, acessar e transformar a árvore de sintaxe abstrata (ou a árvore de análise, respectivamente) do código de entrada. Tanto quanto eu sei, o Sistema matemático sábio, escrito em Python, tem um tipo semelhante de pré -compilador.
Em resposta ao comentário de Wai, aqui está uma solução divertida que encontrei. Primeiro de tudo, para explicar mais uma vez o que faz, suponha que você tenha o seguinte código:
definitions = Structure()
definitions.add_definition('f[x]', 'x*2')
definitions.add_definition('f[z]', 'some_function(z)')
definitions.add_definition('g.i', 'some_object[i].method(param=value)')
onde adicionar definições implica analisar os lados da mão esquerda e os lados da mão direita e fazer outras coisas feias. Agora, uma abordagem (não necessariamente boa, mas certamente divertida) aqui permitiria escrever o código acima da seguinte maneira:
@my_dsl
def definitions():
f[x] = x*2
f[z] = some_function(z)
g.i = some_object[i].method(param=value)
e peça a Python a maior parte da análise sob o capô. A ideia é baseada no simples exec <code> in <environment>
Declaração, mencionada por Ian, com uma adição de hackish. Ou seja, o bytecode da função deve ser ligeiramente ajustado e todas as operações de acesso variável local (load_fast) alteradas para acesso variável do ambiente (load_name).
É mais fácil mostrado do que explicado: http://fouryears.eu/wp-content/uploads/pydsl/
Existem vários truques que você pode querer fazer para torná -lo prático. Por exemplo, no código apresentado no link acima, você não pode usar funções e construções de idiomas incorporadas como loops e instruções se dentro de uma função @MY_DSL. Você pode fazer isso funcionar, no entanto, adicionando mais comportamento à classe Env.
Atualizar. Aqui é uma explicação um pouco mais detalhada da mesma coisa.