Domanda

Non ho davvero prestare più attenzione allo sviluppo di Python 3 di come avrei voluto, e solo appena notato alcune nuove interessanti cambiamenti di sintassi. In particolare da questo SO rispondere parametro di funzione di annotazione:

def digits(x:'nonnegative number') -> "yields number's digits":
    # ...

Non sapendo nulla di questo, ho pensato che poteva forse essere utilizzato per implementare tipizzazione statica in Python!

Dopo alcune ricerche, ci sembrava di essere una discussione molto per quanto riguarda (del tutto facoltativo) tipizzazione statica in Python, come quella di cui al PEP 3107 e "L'aggiunta di tipizzazione statica opzionale alla Python" (e parte 2 )

.. ma, io non sono chiaro quanto questo è progredita. Ci sono delle implementazioni di tipizzazione statica, utilizzando il parametro-annotazioni? Qualcuno di idee parametrizzata tipo rendono in Python 3?

È stato utile?

Soluzione

Grazie per aver letto il mio codice!

In effetti, non è difficile creare un enforcer un'annotazione generica in Python. Ecco il mio prendere:

'''Very simple enforcer of type annotations.

This toy super-decorator can decorate all functions in a given module that have 
annotations so that the type of input and output is enforced; an AssertionError is
raised on mismatch.

This module also has a test function func() which should fail and logging facility 
log which defaults to print. 

Since this is a test module, I cut corners by only checking *keyword* arguments.

'''

import sys

log = print


def func(x:'int' = 0) -> 'str':
    '''An example function that fails type checking.'''
    return x


# For simplicity, I only do keyword args.
def check_type(*args):
    param, value, assert_type = args
    log('Checking {0} = {1} of {2}.'.format(*args))
    if not isinstance(value, assert_type):
        raise AssertionError(
            'Check failed - parameter {0} = {1} not {2}.'
            .format(*args))
    return value

def decorate_func(func):    
    def newf(*args, **kwargs):
        for k, v in kwargs.items():
            check_type(k, v, ann[k])
        return check_type('<return_value>', func(*args, **kwargs), ann['return'])

    ann = {k: eval(v) for k, v in func.__annotations__.items()}
    newf.__doc__ = func.__doc__
    newf.__type_checked = True
    return newf

def decorate_module(module = '__main__'):
    '''Enforces type from annotation for all functions in module.'''
    d = sys.modules[module].__dict__
    for k, f in d.items():
        if getattr(f, '__annotations__', {}) and not getattr(f, '__type_checked', False):
            log('Decorated {0!r}.'.format(f.__name__))
            d[k] = decorate_func(f)


if __name__ == '__main__':
    decorate_module()

    # This will raise AssertionError.
    func(x = 5)

Dato questa semplicità, è strano a prima vista che questa cosa non è mainstream. Tuttavia, credo che ci sono buone ragioni per cui è non è così utile come potrebbe sembrare . In generale, il controllo di tipo aiuta perché se si aggiunge intero e dizionario, è probabile che hai fatto qualche errore manifesto (e se volevi dire qualcosa di ragionevole, è ancora meglio essere espliciti che impliciti ).

Ma nella vita reale si mescolano spesso quantità dello stesso tipo di computer come si è visto dal compilatore, ma chiaramente diverso tipo umano , per esempio il seguente frammento contiene un errore evidente:

height = 1.75 # Bob's height in meters.
length = len(sys.modules) # Number of modules imported by program.
area = height * length # What's that supposed to mean???

Ogni essere umano dovrebbe vedere immediatamente un errore nella riga sopra a condizione che conosce il 'tipo umano' di variabili height e length anche se sembra al computer come perfettamente legale moltiplicazione dei int e float.

C'è di più che si può dire circa le possibili soluzioni a questo problema, ma far rispettare 'i tipi di computer' è apparentemente una mezza soluzione, quindi, almeno a mio parere, è peggio che nessuna soluzione a tutti . E 'lo stesso motivo per cui Sistemi ungherese è una pessima idea, mentre Applicazioni ungherese è un grande. C'è di più a molto informativo posto di Joel Spolsky .

Ora, se qualcuno è stato quello di implementare una sorta di libreria di terze parti Pythonic che assegna automaticamente ai dati del mondo reale il suo tipo umano e poi si prese cura di trasformare quel tipo come width * height -> area e imporre che il check con annotazioni di funzione, penso che sarebbe un tipo di controllo la gente potrebbe davvero usare!

Altri suggerimenti

Come accennato in quel PEP, il controllo di tipo statico è una delle possibili applicazioni che funzionano le annotazioni possono essere utilizzati per, ma sono lasciando a librerie di terze parti per decidere come farlo. Cioè, non ci sta per essere un'implementazione ufficiale nel nucleo python.

Per quanto riguarda le implementazioni di terze parti sono interessati, ci sono alcuni frammenti (come http: // code.activestate.com/recipes/572161/ ), che sembrano fare il lavoro abbastanza bene.

EDIT:

Come nota, voglio ricordare che il controllo il comportamento è preferibile tipo controllo, quindi penso che typechecking statico non è così grande idea. La mia risposta sopra è finalizzato a rispondere alla domanda, non perché mi avrebbe fatto io stesso typechecking in questo modo.

Questa non è una risposta alla domanda direttamente, ma ho scoperto una forchetta Python che aggiunge tipizzazione statica: mypy- lang.org , ovviamente non si può fare affidamento su di essa come è ancora piccolo sforzo, ma interessante.

"tipizzazione statica" in Python può essere attuata solo in modo che il controllo dei tipi è fatto in fase di esecuzione, il che significa che rallenta l'applicazione. Pertanto non si vuole che, come una generalità. Invece si vuole alcuni dei vostri metodi per controllare che sia ingressi. Questo può essere fatto facilmente con normale afferma, o con decoratori se (erroneamente) che tu abbia bisogno un sacco.

C'è anche un'alternativa a controllo di tipo statico, e che è di usare un aspetto oriented architecture componente come The Zope Component Architecture. Invece di controllare il tipo, di adattarlo. Così, invece di:

assert isinstance(theobject, myclass)

si esegue questa operazione:

theobject = IMyClass(theobject)

Se theObject implementa già IMyClass non succede nulla. In caso contrario, un adattatore che avvolge tutto ciò che è di theObject IMyClass sarà guardato in su e utilizzato al posto di theObject. Se non viene trovato nessun adattatore, si ottiene un errore.

Questo, combinato alla dynamicism di Python con il desiderio di avere un tipo specifico in un modo specifico.

Certo, tipizzazione statica sembra un po ' "unpythonic" e io non lo uso per tutto il tempo. Ma ci sono casi (per esempio classi annidate, come nel dominio specifico linguaggio parsing) dove può realmente accelerare il vostro sviluppo.

Poi io preferisco usare beartype spiegato in questo post *. Viene fornito con un repo git, test e una spiegazione che cosa si può e cosa non può fare ... e mi piace il nome;)

* Si prega di non prestare attenzione a sbraitare di Cecil sul perché Python non viene fornito con batterie incluse in questo caso.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top