Vra

Kan jy nuwe stellings byvoeg (soos print, raise, with) na Python se sintaksis?

Sê, om toe te laat..

mystatement "Something"

Of,

new_if True:
    print "example"

Nie soseer as jy behoort, maar eerder as dit moontlik is (tekort daaraan om die python-tolkkode te wysig)

Was dit nuttig?

Oplossing

Jy kan dit nuttig vind - Python internals:voeg 'n nuwe stelling by Python, hier aangehaal:


Hierdie artikel is 'n poging om beter te verstaan ​​hoe die voorkant van Python werk.Net die lees van dokumentasie en bronkode kan 'n bietjie vervelig wees, so ek volg 'n praktiese benadering hier:Ek gaan 'n byvoeg until verklaring aan Python.

Al die kodering vir hierdie artikel is gedoen teen die nuutste Py3k-tak in die Python Mercurial repository spieël.

Die until verklaring

Sommige tale, soos Ruby, het 'n until stelling, wat die aanvulling tot while (until num == 0 gelykstaande is aan while num != 0).In Ruby kan ek skryf:

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

En dit sal druk:

3
2
1

Dus, ek wil 'n soortgelyke vermoë by Python voeg.Dit wil sê om te kan skryf:

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

'n Taalvoorspraak-afwyking

Hierdie artikel poog nie om die byvoeging van 'n until verklaring aan Python.Alhoewel ek dink so 'n stelling sal 'n paar kode duideliker maak, en hierdie artikel wys hoe maklik dit is om by te voeg, respekteer ek Python se filosofie van minimalisme ten volle.Al wat ek hier probeer doen, is regtig 'n bietjie insig in die innerlike werking van Python.

Die wysiging van die grammatika

Python gebruik 'n pasgemaakte ontledergenerator genaamd pgen.Dit is 'n LL(1)-ontleder wat Python-bronkode in 'n ontledingsboom omskakel.Die invoer na die ontledergenerator is die lêer Grammar/Grammar[1].Dit is 'n eenvoudige tekslêer wat die grammatika van Python spesifiseer.

[1]:Van hier af word verwysings na lêers in die Python-bron gegee relatief tot die wortel van die bronboom, wat die gids is waar jy konfigureer en maak om Python te bou.

Twee wysigings moet aan die grammatikalêer gemaak word.Die eerste is om 'n definisie vir die by te voeg until verklaring.Ek het gevind waar die while stelling is gedefinieer (while_stmt), en bygevoeg until_stmt hieronder [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]:Dit demonstreer 'n algemene tegniek wat ek gebruik wanneer ek bronkode wysig waarmee ek nie vertroud is nie: werk volgens ooreenkoms.Hierdie beginsel sal nie al jou probleme oplos nie, maar dit kan beslis die proses vergemaklik.Aangesien alles waarvoor gedoen moet word while ook daarvoor gedoen moet word until, dit dien as 'n redelik goeie riglyn.

Let daarop dat ek besluit het om die uit te sluit else klousule uit my definisie van until, net om dit 'n bietjie anders te maak (en omdat ek eerlikwaar nie van die else klousule van lusse en dink nie dit pas goed by die Zen van Python nie).

Die tweede verandering is om die reël vir te wysig compound_stmt in te sluit until_stmt, soos jy in die brokkie hierbo kan sien.Dis net daarna while_stmt, weer.

Wanneer jy hardloop make na wysiging Grammar/Grammar, let op dat die pgen program word uitgevoer om te hergenereer Include/graminit.h en Python/graminit.c, en dan word verskeie lêers weer saamgestel.

Wysiging van die AST generasie kode

Nadat die Python-ontleder 'n ontledingsboom geskep het, word hierdie boom in 'n AST omgeskakel, aangesien AST's baie makliker om mee te werk in die daaropvolgende stadiums van die samestellingsproses.

So, ons gaan kuier Parser/Python.asdl wat die struktuur van Python se AST's definieer en 'n AST-nodus vir ons nuwe byvoeg until stelling, weer reg onder die while:

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

As jy nou hardloop make, let op dat voordat u 'n klomp lêers saamstel, Parser/asdl_c.py word uitgevoer om C-kode vanaf die AST-definisielêer te genereer.Hierdie Soos Grammar/Grammar) is nog 'n voorbeeld van die Python-bronkode wat 'n mini-taal (met ander woorde 'n DSL) gebruik om programmering te vereenvoudig.Let ook daarop dat sedert Parser/asdl_c.py is 'n Python script, dit is 'n soort van bootstrapping - om Python van nuuts af te bou, moet Python reeds beskikbaar wees.

Terwyl Parser/asdl_c.py het die kode gegenereer om ons nuut gedefinieerde AST-nodus (in die lêers Include/Python-ast.h en Python/Python-ast.c), moet ons steeds die kode skryf wat 'n relevante ontleedboomnodus met die hand daarin omskakel.Dit word in die lêer gedoen Python/ast.c.Daar, 'n funksie met die naam ast_for_stmt omskep ontleed boom nodusse vir stellings in AST nodusse.Weereens, gelei deur ons ou vriend while, spring ons reg in die groot in switch vir die hantering van saamgestelde state en voeg 'n klousule by vir until_stmt:

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

Nou moet ons implementeer ast_for_until_stmt.Hier is dit:

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;
}

Weereens, dit is gekodeer terwyl noukeurig na die ekwivalent gekyk is ast_for_while_stmt, met die verskil dat vir until Ek het besluit om nie die else klousule.Soos verwag, word die AST rekursief geskep, met behulp van ander AST-skepfunksies soos ast_for_expr vir die voorwaarde uitdrukking en ast_for_suite vir die liggaam van die until verklaring.Ten slotte, 'n nuwe nodus met die naam Until word terugbesorg.

Let daarop dat ons toegang tot die parse-tree node kry n met behulp van sommige makro's soos NCH en CHILD.Dit is die moeite werd om te verstaan ​​- hul kode is in Include/node.h.

Afwyking:AST samestelling

Ek het gekies om 'n nuwe tipe AST vir die te skep until stelling, maar eintlik is dit nie nodig nie.Ek kon 'n bietjie werk bespaar het en die nuwe funksionaliteit geïmplementeer het deur die samestelling van bestaande AST-nodes te gebruik, aangesien:

until condition:
   # do stuff

Is funksioneel gelykstaande aan:

while not condition:
  # do stuff

In plaas daarvan om die Until nodus in ast_for_until_stmt, Ek kon 'n geskep het Not nodus met 'n While node as kind.Aangesien die AST-samesteller reeds weet hoe om hierdie nodusse te hanteer, kan die volgende stappe van die proses oorgeslaan word.

Samestelling van AST's in greepkode

Die volgende stap is om die AST in Python-greepkode saam te stel.Die samestelling het 'n intermediêre resultaat wat 'n CFG (Control Flow Graph) is, maar aangesien dieselfde kode dit hanteer, sal ek hierdie detail vir eers ignoreer en dit vir 'n ander artikel laat.

Die kode waarna ons volgende sal kyk is Python/compile.c.Na aanleiding van while, vind ons die funksie compiler_visit_stmt, wat verantwoordelik is vir die samestelling van stellings in greepkode.Ons voeg 'n klousule by vir Until:

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

As jy wonder wat Until_kind is, dit is 'n konstante (eintlik 'n waarde van die _stmt_kind enumeration) outomaties gegenereer vanaf die AST-definisielêer in Include/Python-ast.h.In elk geval, ons bel compiler_until wat natuurlik steeds nie bestaan ​​nie.Ek sal 'n oomblik daarby uitkom.

As jy nuuskierig is soos ek, sal jy dit agterkom compiler_visit_stmt is eienaardig.Geen bedrag van grep-ping die bronboom onthul waar dit genoem word.Wanneer dit die geval is, bly net een opsie oor - C makro-fu.Inderdaad, 'n kort ondersoek lei ons na die VISIT makro gedefinieer in Python/compile.c:

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

Dit word gebruik om op te roep compiler_visit_stmt in compiler_body.Maar terug na ons besigheid...

Soos belowe, hier is compiler_until:

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;
}

Ek het 'n bekentenis om te maak:hierdie kode is nie geskryf op grond van 'n diepgaande begrip van Python-greepkode nie.Soos die res van die artikel is dit in navolging van die familie gedoen compiler_while funksie.Deur dit egter noukeurig te lees, in gedagte hou dat die Python VM stapelgebaseerd is, en kyk na die dokumentasie van die dis module, wat het 'n lys van Python-greepkodes met beskrywings is dit moontlik om te verstaan ​​wat aangaan.

Dis dit, ons is klaar...Is ons nie?

Nadat u al die veranderinge gemaak het en hardloop make, kan ons die nuut saamgestelde Python hardloop en ons nuwe probeer until verklaring:

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

Voila, dit werk!Kom ons kyk na die greepkode wat vir die nuwe stelling geskep is deur die dis module soos volg:

import dis

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

dis.dis(myfoo)

Hier is die resultaat:

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

Die interessantste operasie is nommer 12:as die voorwaarde waar is, spring ons na die lus.Dit is korrekte semantiek vir until.As die sprong nie uitgevoer word nie, hou die lusliggaam aan hardloop totdat dit terugspring na die toestand by operasie 35.

Ek voel goed oor my verandering en probeer toe om die funksie (uitvoering myfoo(3)) in plaas daarvan om sy greepkode te wys.Die resultaat was minder as bemoedigend:

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'

Sjoe...dit kan nie goed wees nie.So wat het verkeerd geloop?

Die geval van die ontbrekende simbooltabel

Een van die stappe wat die Python-samesteller uitvoer wanneer die AST saamgestel word, is om 'n simbooltabel te skep vir die kode wat dit saamstel.Die oproep na PySymtable_Build in PyAST_Compile roep in die simbool tabel module (Python/symtable.c), wat die AST loop op 'n manier soortgelyk aan die kodegenereringsfunksies.Om 'n simbooltabel vir elke omvang te hê, help die samesteller om 'n paar sleutelinligting uit te vind, soos watter veranderlikes globaal is en watter plaaslik tot 'n omvang is.

Om die probleem op te los, moet ons die symtable_visit_stmt funksioneer in Python/symtable.c, voeg kode by vir hantering until stellings, na die soortgelyke kode vir while stellings [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]:Terloops, sonder hierdie kode is daar 'n samestellerwaarskuwing vir Python/symtable.c.Die samesteller merk op dat die Until_kind opsommingswaarde word nie in die skakelstaat van hanteer nie symtable_visit_stmt en kla.Dit is altyd belangrik om te kyk vir samesteller waarskuwings!

En nou is ons regtig klaar.Die samestelling van die bron na hierdie verandering maak die uitvoering van myfoo(3) werk soos verwag.

Afsluiting

In hierdie artikel het ek gedemonstreer hoe om 'n nuwe stelling by Python te voeg.Alhoewel dit nogal 'n bietjie gepeuter in die kode van die Python-samesteller vereis het, was die verandering nie moeilik om te implementeer nie, want ek het 'n soortgelyke en bestaande stelling as 'n riglyn gebruik.

Die Python-samesteller is 'n gesofistikeerde stuk sagteware, en ek beweer nie dat ek 'n kenner daarin is nie.Ek stel egter regtig belang in die interne van Python, en veral die voorkant daarvan.Daarom het ek hierdie oefening 'n baie nuttige metgesel gevind tot teoretiese studie van die samesteller se beginsels en bronkode.Dit sal dien as 'n basis vir toekomstige artikels wat dieper in die samesteller sal kom.

Verwysings

Ek het 'n paar uitstekende verwysings gebruik vir die konstruksie van hierdie artikel.Hier is hulle, in geen spesifieke volgorde nie:

  • PEP 339:Ontwerp van die CPython samesteller - waarskynlik die belangrikste en mees omvattende stuk van amptelik dokumentasie vir die Python-samesteller.Omdat dit baie kort is, toon dit pynlik die skaarste aan goeie dokumentasie van die interne van Python.
  • "Python Compiler Internals" - 'n artikel deur Thomas Lee
  • "Python:Ontwerp en Implementering" - 'n aanbieding deur Guido van Rossum
  • Python (2.5) Virtuele masjien, 'n Begeleide toer - 'n aanbieding deur Peter Tröger

oorspronklike bron

Ander wenke

Een manier om dinge soos hierdie te doen, is om vir wysig die bron en verander dit, die vertaling van jou bykomende verklaring aan luislang. Daar is verskeie probleme wat hierdie benadering sal bring, en ek sal dit nie aanbeveel vir algemene gebruik, maar vir eksperimentering met taal, of spesifieke doel metaprogramming, kan dit Soms nuttig wees.

Byvoorbeeld, kan sê ons wil 'n "myprint" verklaring voer, wat in plaas van druk op die skerm in plaas logs om 'n spesifieke lêer. dit wil sê:

myprint "This gets logged to file"

gelykstaande aan sou wees

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

Daar is verskeie opsies oor hoe om dit te doen die vervanging van regex vervanging te genereer 'n AST, tot die skryf van jou eie ontleder, afhangende van hoe naby jou sintaksis wedstryde bestaande luislang. 'N Goeie intermediêre benadering is om die tokenizer module gebruik. Dit moet toelaat om nuwe sleutelwoorde, beheerstrukture ens byvoeg terwyl die interpretasie van die bron soortgelyk aan die luislang tolk, dus die skade ru regex oplossings sou veroorsaak te vermy. Vir bogenoemde "myprint", kan jy die volgende transformasie-kode skryf:

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

(Dit maak wel myprint effektief 'n navraag, so gebruik as 'n veranderlike elders sal waarskynlik veroorsaak probleme)

Die probleem dan is hoe om dit te gebruik sodat jou kode is bruikbare uit luislang. Een manier sou net wees om jou eie invoer funksie te skryf, en gebruik dit om te laai kode geskryf in jou persoonlike taal. dit wil sê:

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

Dit vereis dat jy jou persoonlike kode anders hanteer van normale python modules egter. dit wil sê "some_mod = myimport("some_mod.py")" eerder as "import some_mod"

Nog 'n redelik netjies (al is dit mag nou wel) oplossing is om 'n persoonlike enkodering (Sien PEP skep 263 ) as hierdie resep demonstreer. Jy kan dit te implementeer as:

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)

En ná hierdie kode kry hardloop (bv jy kan plaas dit in jou .pythonrc of site.py.) Enige kode wat begin met die opmerking "# kodering: mylang" sal outomaties vertaal deur bogenoemde preprocessing stap. bv.

# 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")

Valkuilen:

Daar is probleme met die voorverwerker benadering, soos jy waarskynlik vertroud is met as jy gewerk het met die C preprocessor sal wees. Die belangrikste een is debugging. Alle luislang sien is die preprocessed lêer wat daardie teks gedruk in die stapel spoor ens sal verwys na dit beteken. As jy beduidende vertaling het uitgevoer, kan hierdie baie anders as jou bronteks wees. Die voorbeeld hierbo nie reëlnommers ens verander, so sal nie veel verskillend wees, maar hoe meer jy dit verander, hoe moeiliker sal dit wees om uit te vind.

Ja, tot 'n mate is dit moontlik. Daar is 'n module daar buite wat sys.settrace() gebruik om goto en comefrom "dokumente" te implementeer:

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"

Kort van die verandering en hercompileren die bronkode (wat is moontlik met open source), die verandering van die basis taal is nie regtig moontlik.

Selfs as jy doen heropstel die bron, sou dit nie luislang wees, net jou gekap-up verander weergawe wat jy nodig het baie versigtig wees om nie foute te voer in te wees.

Maar ek is nie seker hoekom jy wil om. Python se objekgeoriënteerde eienskappe maak dit eenvoudig om soortgelyke resultate met die taal te bereik soos dit nou is.

General antwoord: jy nodig het om vir wysig jou bronkodelêers.

Meer spesifieke antwoord: installeer EasyExtend , en gaan deur volgende stappe

i) Skep 'n nuwe Langlet (verlenging taal)

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

Sonder bykomende spesifikasie 'n klomp van die lêers moet geskep onder EasyExtend / langlets / mystmts /.

ii) Open mystmts / parsedef / Grammar.ext en voeg volgende reëls

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

Dit is voldoende om die sintaksis van jou nuwe verklaring definieer. Die small_stmt nie-terminale is deel van die Python grammatika en dit is die plek waar die nuwe verklaring is verslaaf in. Die ontleder sal nou die nuwe verklaring maw 'n bron lêer met dit sal ontleed word erken. Die samesteller sal dit al verwerp omdat dit nog te omskep word in 'n geldige Python.

iii) Nou moet semantiek van die verklaring te voeg. Vir hierdie een het om te verander  msytmts / langlet.py en voeg 'n my_stmt node besoeker.

 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 te langlets / mystmts en tipe

python run_mystmts.py

Nou 'n sessie sal begin en die nuut gedefinieerde verklaring kan gebruik word:

__________________________________________________________________________________

 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

'n Hele paar stappe om 'n triviale verklaring te kom, of hoe? Daar is nie 'n API nog waarmee 'n eenvoudige dinge te definieer sonder om te bekommer oor grammatikas. Maar EE is baie betroubaar modulo n paar foute. So dit is net 'n kwessie van tyd dat 'n API na vore waarmee programmeerders definieer gerieflike dinge soos voeg sels operateurs of klein state met behulp van net gerieflik OO programmering. Vir meer komplekse dinge soos inbedding hele tale in Python met behulp van die bou van 'n Langlet is daar geen manier om te gaan om 'n volledige grammatika benadering.

Hier is 'n baie eenvoudige, maar crappy manier om nuwe state te voeg, in net interpretatiewe af . Ek is dit met behulp van vir klein 1-letter opdragte vir die wysiging van gene notas met behulp van slegs sys.displayhook, maar net sodat ek kan hierdie vraag wat ek bygevoeg sys.excepthook vir die sintaksfoute sowel beantwoord. Laasgenoemde is regtig lelik, haal die rou-kode van die read line buffer. Die voordeel is, dit is trivially maklik om nuwe state op hierdie manier te voeg.


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

Ek het 'n gids gevind word op die toevoeging van nuwe state:

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

In beginsel, om nuwe state te voeg, moet jy Python/ast.c (onder andere) wysig en heropstel die luislang binêre.

Hoewel dit moontlik is, doen nie. Jy kan byna alles te bereik via funksies en klasse (wat gewoond vereis dat mense luislang heropstel net om jou script loop ..)

Dit is moontlik om dit te doen deur gebruik te maak EasyExtend :

  

EasyExtend (EE) is 'n voorverwerker   generator en metaprogramming   raamwerk geskryf in suiwer Python en   geïntegreer met CPython. Die hoof   doel van EasyExtend is die skepping   van uitbreiding tale maw die toevoeging   persoonlike sintaksis en semantiek te Python.

Nie sonder wysiging van die tolk. Ek weet baie van tale in die afgelope paar jaar het al beskryf as "extensible", maar nie in die manier waarop jy beskryf. Jy brei Python deur die toevoeging van funksies en klasse.

Daar is 'n taal wat gebaseer is op luislang genoem Logix waarmee jy kan sulke dinge doen. Dit het nie onder ontwikkeling vir 'n rukkie, maar die eienskappe wat jy gevra vir nie werk met die nuutste weergawe.

Sommige dinge kan gedoen word met ontwerpers. Kom ons bv aanvaar, Python het geen with verklaring. Ons kan dan implementeer 'n soortgelyke gedrag soos volg:

# ====== 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())

Dit is 'n mooi onrein oplossing egter as hier gedoen. Veral die gedrag waar die versierder noem die funksie en stel _ om None is onverwags. Om duidelikheid te verkry: Dit versierder is gelykstaande aan die skryf

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

en ontwerpers word normaalweg verwag om te verander, nie uit te voer, funksies.

Ek gebruik so 'n metode voor in 'n script waar ek moes tydelik stel die werk gids vir 'n paar funksies.

Dit is nie presies die toevoeging van nuwe state tot die taal sintaksis, maar makros is 'n kragtige instrument: https: // GitHub. com / lihaoyi / macropy

Tien jaar gelede kon jy nie, en ek twyfel dat daar verander. Dit was egter nie so moeilik om die sintaksis terug dan verander as jy bereid is om luislang heropstel, en ek twyfel dat daar verander, óf.

Gelisensieer onder: CC-BY-SA met toeskrywing
Nie verbonde aan StackOverflow
scroll top