it-swarm.com.de

Die beste Möglichkeit, Funktionsargumente in Python zu überprüfen

Ich suche nach einer effizienten Möglichkeit, Variablen einer Python-Funktion zu überprüfen. Zum Beispiel möchte ich Argumenttyp und -wert überprüfen. Gibt es dafür ein Modul? Oder sollte ich etwas wie Dekorateure oder eine bestimmte Redewendung verwenden?

def my_function(a, b, c):
    """an example function I'd like to check the arguments of."""
    # check that a is an int
    # check that 0 < b < 10
    # check that c is not an empty string
44
carmellose

Das Pythonic-Idiom ist eindeutig document, was die Funktion erwartet, und dann einfach versuchen, das zu verwenden, was an Ihre Funktion übergeben wird, und entweder die Ausnahmen verbreiten oder Attributfehler abfangen und stattdessen eine TypeError auslösen. Die Typprüfung sollte so weit wie möglich vermieden werden, da dies der Duck-Typing-Methode widerspricht. Wertetests können - abhängig vom Kontext - in Ordnung sein.

Der einzige Ort, an dem die Validierung wirklich Sinn macht, ist der Einstiegspunkt des Systems oder des Subsystems, z. B. Webformulare, Befehlszeilenargumente usw. Überall sonst, sofern Ihre Funktionen ordnungsgemäß dokumentiert sind, ist der Aufrufer dafür verantwortlich, geeignete Argumente zu übergeben.

52

Die Typprüfung ist im Allgemeinen nicht Pythonic. In Python ist es üblicher, duck einzugeben . Beispiel:

Nehmen Sie in Ihrem Code an, dass das Argument (in Ihrem Beispiel a) wie eine int ausgeführt wird und quacksiert wie eine int. Zum Beispiel:

def my_function(a):
    return a + 7

Dies bedeutet, dass Ihre Funktion nicht nur mit Ganzzahlen arbeitet, sondern auch mit Floats und jeder benutzerdefinierten Klasse, für die die __add__-Methode definiert ist. Wenn Sie oder eine andere Person Ihre Funktion erweitern möchten, müssen Sie weniger (manchmal auch nichts) tun mit etwas anderem arbeiten. In einigen Fällen benötigen Sie jedoch möglicherweise eine int. Dann könnten Sie so etwas tun:

def my_function(a):
    b = int(a) + 7
    c = (5, 6, 3, 123541)[b]
    return c

und die Funktion funktioniert weiterhin für alle a, die die __int__-Methode definieren.

Als Antwort auf Ihre anderen Fragen halte ich es für das Beste (wie andere Antworten gesagt haben, tun Sie dies entweder:

def my_function(a, b, c):
    assert 0 < b < 10
    assert c        # A non-empty string has the Boolean value True

oder

def my_function(a, b, c):
    if 0 < b < 10:
        # Do stuff with b
    else:
        raise ValueError
    if c:
        # Do stuff with c
    else:
        raise ValueError

Einige Typen, die Dekorateure überprüften:

import inspect

def checkargs(function):
    def _f(*arguments):
        for index, argument in enumerate(inspect.getfullargspec(function)[0]):
            if not isinstance(arguments[index], function.__annotations__[argument]):
                raise TypeError("{} is not of type {}".format(arguments[index], function.__annotations__[argument]))
        return function(*arguments)
    _f.__doc__ = function.__doc__
    return _f

def coerceargs(function):
    def _f(*arguments):
        new_arguments = []
        for index, argument in enumerate(inspect.getfullargspec(function)[0]):
            new_arguments.append(function.__annotations__[argument](arguments[index]))
        return function(*new_arguments)
    _f.__doc__ = function.__doc__
    return _f

if __== "__main__":
    @checkargs
    def f(x: int, y: int):
        """
        A doc string!
        """
        return x, y

    @coerceargs
    def g(a: int, b: int):
        """
        Another doc string!
        """
        return a + b

    print(f(1, 2))
    try:
        print(f(3, 4.0))
    except TypeError as e:
        print(e)

    print(g(1, 2))
    print(g(3, 4.0))
16
rlms

Eine Möglichkeit ist, assert zu verwenden:

def myFunction(a,b,c):
    "This is an example function I'd like to check arguments of"
    assert isinstance(a, int), 'a should be an int'
    # or if you want to allow whole number floats: assert int(a) == a
    assert b > 0 and b < 10, 'b should be betwen 0 and 10'
    assert isinstance(c, str) and c, 'c should be a non-empty string'
10
Matthew Plourde

Sie können Type Enforcement akzeptieren/zurückgeben, Dekorateure von PythonDecoratorLibrary

@accepts(int, int, float)
def myfunc(i1, i2, i3):
    pass
6
DominikStyp

Es gibt verschiedene Möglichkeiten, zu prüfen, was eine Variable in Python ist. Um nur einige aufzulisten:

  • Die isinstance(obj, type)-Funktion nimmt Ihre Variable obj und gibt Ihnen True an, ob es sich um denselben Typ der type handelt, die Sie aufgelistet haben.

  • issubclass(obj, class)-Funktion, die eine Variable obj aufnimmt und Ihnen True gibt, wenn obj eine Unterklasse von class ist. Zum Beispiel würde issubclass(Rabbit, Animal) Ihnen einen True-Wert geben

  • hasattr ist ein weiteres Beispiel, das durch diese Funktion veranschaulicht wird, super_len:


def super_len(o):
    if hasattr(o, '__len__'):
        return len(o)

    if hasattr(o, 'len'):
        return o.len

    if hasattr(o, 'fileno'):
        try:
            fileno = o.fileno()
        except io.UnsupportedOperation:
            pass
        else:
            return os.fstat(fileno).st_size

    if hasattr(o, 'getvalue'):
        # e.g. BytesIO, cStringIO.StringI
        return len(o.getvalue())

hasattr neigt mehr zur Enten-Typisierung und etwas, das normalerweise mehr Pythonic ist, aber dieser Begriff ist der Meinung.

Nur als Hinweis, assert-Anweisungen werden normalerweise zum Testen verwendet, ansonsten verwenden Sie einfach if/else-Anweisungen.

5
Games Brainiac

Ich habe in letzter Zeit einige Nachforschungen zu diesem Thema angestellt, da ich mit den vielen Bibliotheken , die ich dort gefunden habe, nicht zufrieden war.

Am Ende habe ich eine Bibliothek entwickelt, um das anzusprechen. Sie heißt valid8 . Wie in der Dokumentation erläutert, handelt es sich dabei hauptsächlich um die Validierung von Werten (obwohl sie auch mit einfachen Typüberprüfungsfunktionen geliefert wird), und Sie möchten sie möglicherweise mit einem PEP484-basierten Type Checker wie enforce oder pytypes verknüpfen .

So würden Sie die Validierung mit valid8 alleine durchführen (und mini_lambda , um die Validierungslogik zu definieren - ist aber nicht zwingend) in Ihrem Fall:

# for type validation
from numbers import Integral
from valid8 import instance_of

# for value validation
from valid8 import validate_arg
from mini_lambda import x, s, Len

@validate_arg('a', instance_of(Integral))
@validate_arg('b', (0 < x) & (x < 10))
@validate_arg('c', instance_of(str), Len(s) > 0)
def my_function(a: Integral, b, c: str):
    """an example function I'd like to check the arguments of."""
    # check that a is an int
    # check that 0 < b < 10
    # check that c is not an empty string

# check that it works
my_function(0.2, 1, 'r')  # InputValidationError for 'a' HasWrongType: Value should be an instance of <class 'numbers.Integral'>. Wrong value: [0.2].
my_function(0, 0, 'r')    # InputValidationError for 'b' [(x > 0) & (x < 10)] returned [False]
my_function(0, 1, 0)      # InputValidationError for 'c' Successes: [] / Failures: {"instance_of_<class 'str'>": "HasWrongType: Value should be an instance of <class 'str'>. Wrong value: [0]", 'len(s) > 0': "TypeError: object of type 'int' has no len()"}.
my_function(0, 1, '')     # InputValidationError for 'c' Successes: ["instance_of_<class 'str'>"] / Failures: {'len(s) > 0': 'False'}

Und dies ist dasselbe Beispiel, bei dem PEP484-Typhinweise verwendet und die Typüberprüfung an enforce delegiert werden:

# for type validation
from numbers import Integral
from enforce import runtime_validation, config
config(dict(mode='covariant'))  # type validation will accept subclasses too

# for value validation
from valid8 import validate_arg
from mini_lambda import x, s, Len

@runtime_validation
@validate_arg('b', (0 < x) & (x < 10))
@validate_arg('c', Len(s) > 0)
def my_function(a: Integral, b, c: str):
    """an example function I'd like to check the arguments of."""
    # check that a is an int
    # check that 0 < b < 10
    # check that c is not an empty string

# check that it works
my_function(0.2, 1, 'r')  # RuntimeTypeError 'a' was not of type <class 'numbers.Integral'>
my_function(0, 0, 'r')    # InputValidationError for 'b' [(x > 0) & (x < 10)] returned [False]
my_function(0, 1, 0)      # RuntimeTypeError 'c' was not of type <class 'str'>
my_function(0, 1, '')     # InputValidationError for 'c' [len(s) > 0] returned [False].
2
smarie

Normalerweise machst du so etwas:

def myFunction(a,b,c):
   if not isinstance(a, int):
      raise TypeError("Expected int, got %s" % (type(a),))
   if b <= 0 or b >= 10:
      raise ValueError("Value %d out of range" % (b,))
   if not c:
      raise ValueError("String was empty")

   # Rest of function
2
Mats Kindahl

Dies prüft den Typ der Eingabeargumente beim Aufruf der Funktion:

def func(inp1:int=0,inp2:str="*"):

    for item in func.__annotations__.keys():
        assert isinstance(locals()[item],func.__annotations__[item])

    return (something)

first=7
second="$"
print(func(first,second))

Überprüfen Sie auch mit second=9 (es muss ein Assertionsfehler gegeben werden)

1
Mahdi Ghelichi

Wenn Sie die Validierung für mehrere Funktionen durchführen möchten, können Sie die Logik innerhalb eines Dekorators folgendermaßen hinzufügen:

def deco(func):
     def wrapper(a,b,c):
         if not isinstance(a, int)\
            or not isinstance(b, int)\
            or not isinstance(c, str):
             raise TypeError
         if not 0 < b < 10:
             raise ValueError
         if c == '':
             raise ValueError
         return func(a,b,c)
     return wrapper

und benutze es:

@deco
def foo(a,b,c):
    print 'ok!'

Hoffe das hilft!

0
Paulo Bu

Dies ist keine Lösung für Sie, aber wenn Sie die Funktionsaufrufe auf bestimmte Parametertypen beschränken möchten, müssen Sie den PROATOR {The Python Function-Prototypvalidator} verwenden. Sie können auf den folgenden Link verweisen. https://github.com/mohit-thakur-721/proator

0
Mohit Thakur

Wenn Sie **kwargs, *args und normale Argumente auf einmal überprüfen möchten, können Sie die Funktion locals() als erste Anweisung in Ihrer Funktionsdefinition verwenden, um ein Wörterbuch der Argumente abzurufen.

Verwenden Sie dann type(), um die Argumente zu untersuchen, beispielsweise während Sie das Diktat durchlaufen.

def myfunc(my, args, to, this, function, **kwargs):
    d = locals()
    assert(type(d.get('x')) == str)
    for x in d:
        if x != 'x':
            assert(type(d[x]) == x
    for x in ['a','b','c']:
        assert(x in d)

    whatever more...
0
Jo So
def someFunc(a, b, c):
    params = locals()
    for _item in params:
        print type(params[_item]), _item, params[_item]

Demo:

>> someFunc(1, 'asd', 1.0)
>> <type 'int'> a 1
>> <type 'float'> c 1.0
>> <type 'str'> b asd

mehr über Einheimische ()

0
FallenAngel