Was sind gute Verwendungsmöglichkeiten für Python3s "Funktionsanmerkungen"?


159

Funktionsanmerkungen: PEP-3107

Ich bin auf einen Codeausschnitt gestoßen, der die Funktionsanmerkungen von Python3 demonstriert. Das Konzept ist einfach, aber ich kann mir nicht vorstellen, warum diese in Python3 implementiert wurden oder für welche Zwecke sie gut geeignet sind. Vielleicht kann mich SO aufklären?

Wie es funktioniert:

def foo(a: 'x', b: 5 + 6, c: list) -> max(2, 9):
    ... function body ...

Alles, was nach einem Argument auf den Doppelpunkt folgt, ist eine 'Annotation', und die darauf folgenden Informationen ->sind eine Annotation für den Rückgabewert der Funktion.

foo.func_annotations würde ein Wörterbuch zurückgeben:

{'a': 'x',
 'b': 11,
 'c': list,
 'return': 9}

Welche Bedeutung hat es, dies zur Verfügung zu haben?



6
@ SilentGhost: Leider sind viele der Links zu den tatsächlichen Anwendungsfällen unterbrochen. Gibt es einen Ort, an dem der Inhalt gespeichert wurde oder für immer verschwunden ist?
Max

16
sollte nicht foo.func_annotations sein foo.__annotations__in python3?
Zhangxaochen

2
Anmerkungen haben keine besondere Bedeutung. Das einzige, was Python tut, ist, sie in das Anmerkungswörterbuch aufzunehmen . Jede andere Aktion liegt bei Ihnen.
N Randhawa

was tut def foo(a: 'x', b: 5 + 6, c: list) -> max(2, 9):bedeuten?
Ali SH

Antworten:


90

Ich finde das eigentlich toll.

Ich komme aus dem akademischen Bereich und kann Ihnen sagen, dass sich Anmerkungen als von unschätzbarem Wert erwiesen haben, um intelligente statische Analysegeräte für Sprachen wie Java zu ermöglichen. Sie können beispielsweise Semantiken wie Statusbeschränkungen, Threads, auf die zugegriffen werden darf, Architekturbeschränkungen usw. definieren, und es gibt eine ganze Reihe von Tools, die diese dann lesen und verarbeiten können, um Sicherheiten zu bieten, die über das hinausgehen, was Sie von den Compilern erhalten. Sie könnten sogar Dinge schreiben, die Vor- / Nachbedingungen überprüfen.

Ich bin der Meinung, dass so etwas in Python wegen seiner schwächeren Typisierung besonders benötigt wird, aber es gab wirklich keine Konstrukte, die dies einfach und Teil der offiziellen Syntax machten.

Es gibt andere Verwendungszwecke für Anmerkungen, die nicht sicher sind. Ich kann sehen, wie ich meine Java-basierten Tools auf Python anwenden kann. Zum Beispiel habe ich ein Tool, mit dem Sie Methoden spezielle Warnungen zuweisen können und das Ihnen beim Aufrufen Hinweise gibt, dass Sie deren Dokumentation lesen sollten (Stellen Sie sich beispielsweise vor, Sie haben eine Methode, die nicht mit einem negativen Wert aufgerufen werden darf, aber es ist nicht intuitiv aus dem Namen). Mit Anmerkungen könnte ich technisch so etwas für Python schreiben. Ebenso kann ein Tool geschrieben werden, das Methoden in einer großen Klasse basierend auf Tags organisiert, wenn eine offizielle Syntax vorhanden ist.


34
ISTM Dies sind theoretische Vorteile, die nur realisiert werden können, wenn die Standardbibliothek und die Module von Drittanbietern alle Funktionsanmerkungen verwenden und diese mit konsistenter Bedeutung verwenden und ein durchdachtes Anmerkungssystem verwenden. Bis zu diesem Tag (der niemals kommen wird) werden die Funktionsanmerkungen von Python hauptsächlich in den in den anderen Antworten beschriebenen einmaligen Verwendungen verwendet. Vorerst können Sie intelligente statische Analysatoren, Compiler-Zusicherungen, Java-basierte Toolketten usw. vergessen
Raymond Hettinger

4
Auch ohne alles, was Funktionsanmerkungen verwendet, können Sie sie für die statische Analyse innerhalb von Code verwenden, der sie in seinen Eingaben hat und anderen Code aufruft, der ähnlich mit Anmerkungen versehen ist. Innerhalb eines größeren Projekts oder einer größeren Codebasis kann dies immer noch ein sehr nützlicher Code sein, um eine auf Anmerkungen basierende statische Analyse durchzuführen.
GPS

1
AFAICT, Sie können dies alles mit Dekorateuren tun, die vor Anmerkungen stehen. Daher sehe ich den Nutzen immer noch nicht. Ich habe eine etwas andere Sicht
allyourcode

9
Im schnellen Vorlauf bis 2015 beweisen python.org/dev/peps/pep-0484 und mypy-lang.org , dass alle Neinsager falsch liegen.
Mauricio Scheffer

1
Es zeigt auch den Python-Einfluss auf Swift noch mehr.
Uchuugaka

92

Funktionsanmerkungen sind das, was Sie daraus machen.

Sie können zur Dokumentation verwendet werden:

def kinetic_energy(mass: 'in kilograms', velocity: 'in meters per second'):
     ...

Sie können zur Überprüfung der Vorbedingungen verwendet werden:

def validate(func, locals):
    for var, test in func.__annotations__.items():
        value = locals[var]
        msg = 'Var: {0}\tValue: {1}\tTest: {2.__name__}'.format(var, value, test)
        assert test(value), msg


def is_int(x):
    return isinstance(x, int)

def between(lo, hi):
    def _between(x):
            return lo <= x <= hi
    return _between

def f(x: between(3, 10), y: is_int):
    validate(f, locals())
    print(x, y)


>>> f(0, 31.1)
Traceback (most recent call last):
   ... 
AssertionError: Var: y  Value: 31.1 Test: is_int

Unter http://www.python.org/dev/peps/pep-0362/ finden Sie auch eine Möglichkeit, die Typprüfung zu implementieren.


18
Wie ist dies besser als ein Dokumentstring für die Dokumentation oder eine explizite Typprüfung in der Funktion? Dies scheint die Sprache ohne Grund zu komplizieren.
Endolith

10
@endolith Wir können sicherlich auf Funktionsanmerkungen verzichten. Sie bieten lediglich eine Standardmethode für den Zugriff auf die Anmerkungen. Das macht sie zugänglich für Hilfe () und für Tooltipps und macht sie für die Selbstbeobachtung verfügbar.
Raymond Hettinger

4
Anstatt um Zahlen vorbei Sie Typen erstellen könnte Massund Velocitystattdessen.
Rechtsfalte

1
Um dies vollständig zu demonstrieren, müsste ich def kinetic_energy(mass: 'in kilograms', velocity: 'in meters per second') -> float:auch den Rückgabetyp anzeigen. Dies ist meine Lieblingsantwort hier.
Tommy

Gibt es eine Möglichkeit, die returnAnmerkung mithilfe Ihres Codes zu überprüfen ? Es scheint nicht inlocals
user189728

46

Dies ist eine sehr späte Antwort, aber AFAICT, die derzeit beste Verwendung von Funktionsanmerkungen, ist PEP-0484 und MyPy .

Mypy ist eine optionale statische Typprüfung für Python. Sie können Ihren Python-Programmen mithilfe des bevorstehenden Standards für Typanmerkungen, die in Python 3.5 Beta 1 (PEP 484) eingeführt wurden, Typhinweise hinzufügen und diese mithilfe von mypy statisch überprüfen.

So verwendet:

from typing import Iterator

def fib(n: int) -> Iterator[int]:
    a, b = 0, 1
    while a < n:
        yield a
        a, b = b, a + b

2
Weitere Beispiele hier Mypy Beispiele und hier, wie Sie von
El Ruso

Siehe auch pytype - der andere statische Analysator, der unter Berücksichtigung von PEP-0484 gebaut wird.
GPS

Leider wird der Typ nicht erzwungen. Wenn ich list(fib('a'))mit Ihrer Beispielfunktion tippe, akzeptiert Python 3.7 das Argument gerne und beschwert sich darüber, dass es keine Möglichkeit gibt, einen String und einen Int zu vergleichen.
Denis de Bernardy

@DenisdeBernardy Wie PEP-484 erklärt, bietet Python nur Typanmerkungen. Um Typen durchzusetzen, müssen Sie mypy verwenden.
Dustin Wyatt

23

Um ein konkretes Beispiel für eine gute Verwendung aus meiner Antwort hier hinzuzufügen , kann in Verbindung mit Dekorateuren ein einfacher Mechanismus für Multimethoden durchgeführt werden.

# This is in the 'mm' module

registry = {}
import inspect

class MultiMethod(object):
    def __init__(self, name):
        self.name = name
        self.typemap = {}
    def __call__(self, *args):
        types = tuple(arg.__class__ for arg in args) # a generator expression!
        function = self.typemap.get(types)
        if function is None:
            raise TypeError("no match")
        return function(*args)
    def register(self, types, function):
        if types in self.typemap:
            raise TypeError("duplicate registration")
        self.typemap[types] = function

def multimethod(function):
    name = function.__name__
    mm = registry.get(name)
    if mm is None:
        mm = registry[name] = MultiMethod(name)
    spec = inspect.getfullargspec(function)
    types = tuple(spec.annotations[x] for x in spec.args)
    mm.register(types, function)
    return mm

und ein Anwendungsbeispiel:

from mm import multimethod

@multimethod
def foo(a: int):
    return "an int"

@multimethod
def foo(a: int, b: str):
    return "an int and a string"

if __name__ == '__main__':
    print("foo(1,'a') = {}".format(foo(1,'a')))
    print("foo(7) = {}".format(foo(7)))

Dies kann durch Hinzufügen der Typen zum Dekorateur erfolgen, wie Guidos ursprünglicher Beitrag zeigt. Das Kommentieren der Parameter selbst ist jedoch besser, da die Möglichkeit einer falschen Übereinstimmung von Parametern und Typen vermieden wird.

Hinweis : In Python können Sie auf die Anmerkungen zugreifen, function.__annotations__anstatt function.func_annotationsden func_*Stil in Python 3 zu entfernen.


2
Interessante Anwendung, obwohl ich befürchte, function = self.typemap.get(types)dass sie nicht funktioniert, wenn Unterklassen beteiligt sind. In diesem Fall müssten Sie wahrscheinlich die typemapVerwendung durchlaufen isinnstance. Ich frage mich, ob @overloaddas richtig
gehandhabt

Ich denke, das ist kaputt, wenn die Funktion einen Rückgabetyp hat
Zenna

1
Das __annotations__ ist eine dict, die die Reihenfolge der Argumente nicht sicherstellt, daher schlägt dieses Snippet manchmal fehl. Ich würde empfehlen, das types = tuple(...)auf spec = inspect.getfullargspec(function)dann zu ändern types = tuple([spec.annotations[x] for x in spec.args]).
Xoolive

Sie sind ganz richtig, @xoolive. Warum bearbeiten Sie die Antwort nicht, um Ihr Update hinzuzufügen?
Muhammad Alkarouri

@ xoolive: Ich habe es bemerkt. Manchmal verwenden die Redakteure eine schwere Hand bei der Verwaltung der Änderungen. Ich habe die Frage so bearbeitet, dass sie Ihren Fix enthält. Eigentlich habe ich darüber diskutiert , aber es gibt keine Möglichkeit, das Update abzulehnen. Danke übrigens für die Hilfe.
Muhammad Alkarouri

22

Uri hat bereits eine richtige Antwort gegeben, daher hier eine weniger ernste: So können Sie Ihre Docstrings kürzer machen.


2
Liebe es. +1. Letztendlich ist das Schreiben von Docstrings jedoch immer noch die erste Möglichkeit, meinen Code lesbar zu machen. Wenn Sie jedoch eine statische oder dynamische Überprüfung implementieren, ist es schön, diese zu haben. Vielleicht finde ich eine Verwendung dafür.
Warren P

8
Ich empfehle nicht, Anmerkungen als Ersatz für einen Args: -Abschnitt oder @ param-Zeilen oder ähnliches in Ihren Dokumentzeichenfolgen zu verwenden (egal welches Format Sie verwenden). Dokumentationsanmerkungen sind zwar ein hübsches Beispiel, beeinträchtigen jedoch die potenzielle Leistung von Anmerkungen, da sie anderen leistungsstärkeren Verwendungszwecken im Wege stehen könnten. Außerdem können Sie zur Laufzeit keine Anmerkungen weglassen, um den Speicherverbrauch (Python -OO) zu reduzieren, wie dies bei Dokumentzeichenfolgen und Assert-Anweisungen der Fall ist.
GPS

2
@gps: Wie ich schon sagte, es war eine weniger ernsthafte Antwort.
JAB

2
In aller Ernsthaftigkeit ist es eine viel bessere Möglichkeit, die von Ihnen erwarteten Typen zu dokumentieren, während Sie sich weiterhin an DuckTyping halten.
Marc

1
@gps Ich bin mir nicht sicher, ob der Speicherverbrauch von Dokumentzeichenfolgen in 99,999% der Fälle Anlass zur Sorge gibt.
Tommy

13

Als ich das erste Mal Anmerkungen sah, fand ich "großartig! Endlich kann ich mich für eine Typprüfung entscheiden!" Natürlich hatte ich nicht bemerkt, dass Anmerkungen nicht wirklich erzwungen werden.

Also habe ich beschlossen , einen einfachen Funktionsdekorateur zu schreiben, um sie durchzusetzen :

def ensure_annotations(f):
    from functools import wraps
    from inspect import getcallargs
    @wraps(f)
    def wrapper(*args, **kwargs):
        for arg, val in getcallargs(f, *args, **kwargs).items():
            if arg in f.__annotations__:
                templ = f.__annotations__[arg]
                msg = "Argument {arg} to {f} does not match annotation type {t}"
                Check(val).is_a(templ).or_raise(EnsureError, msg.format(arg=arg, f=f, t=templ))
        return_val = f(*args, **kwargs)
        if 'return' in f.__annotations__:
            templ = f.__annotations__['return']
            msg = "Return value of {f} does not match annotation type {t}"
            Check(return_val).is_a(templ).or_raise(EnsureError, msg.format(f=f, t=templ))
        return return_val
    return wrapper

@ensure_annotations
def f(x: int, y: float) -> float:
    return x+y

print(f(1, y=2.2))

>>> 3.2

print(f(1, y=2))

>>> ensure.EnsureError: Argument y to <function f at 0x109b7c710> does not match annotation type <class 'float'>

Ich habe es der Ensure- Bibliothek hinzugefügt .


Ich habe die gleiche Enttäuschung, nachdem ich davon überzeugt war, dass Python endlich eine Typprüfung hat. Muss endlich mit der hausgemachten Implementierung der Typprüfung fortfahren.
Hibou57

3

Es ist lange her, dass dies gestellt wurde, aber das in der Frage angegebene Beispiel-Snippet stammt (wie auch dort angegeben) aus PEP 3107, und am Ende dieses PEP-Beispiels werden auch Anwendungsfälle angegeben, die die Frage aus PEPs-Sicht von beantworten könnten Aussicht ;)

Das Folgende wird aus PEP3107 zitiert

Anwendungsfälle

Im Verlauf der Erörterung von Anmerkungen wurde eine Reihe von Anwendungsfällen angesprochen. Einige davon werden hier vorgestellt, gruppiert nach der Art der Informationen, die sie vermitteln. Ebenfalls enthalten sind Beispiele für vorhandene Produkte und Pakete, die Anmerkungen verwenden könnten.

  • Eingabe von Tippinformationen
    • Typprüfung ([3], [4])
    • Lassen Sie IDEs zeigen, welche Typen eine Funktion erwartet und zurückgibt ([17])
    • Funktionsüberladung / generische Funktionen ([22])
    • Fremdsprachige Brücken ([18], [19])
    • Anpassung ([21], [20])
    • Prädikatenlogikfunktionen
    • Datenbankabfragezuordnung
    • RPC-Parameter-Marshalling ([23])
  • Andere Informationen
    • Dokumentation für Parameter und Rückgabewerte ([24])

Weitere Informationen zu bestimmten Punkten (sowie deren Referenzen) finden Sie im PEP.


Ich würde mich sehr freuen, wenn Downvoter zumindest einen kurzen Kommentar hinterlassen, der die Downvote verursacht hat. Dies würde (zumindest mir) sehr helfen, mich zu verbessern.
Klaas

2

Python 3.X (nur) verallgemeinert auch die Funktionsdefinition, damit Argumente und Rückgabewerte mit Objektwerten zur Verwendung in Erweiterungen kommentiert werden können .

Seine META-Daten zu erklären, um die Funktionswerte genauer zu beschreiben.

Anmerkungen werden wie :valuenach dem Argumentnamen und vor einem Standard sowie ->valuenach der Argumentliste codiert .

Sie werden in einem __annotations__Attribut der Funktion gesammelt , aber von Python selbst nicht als besonders behandelt:

>>> def f(a:99, b:'spam'=None) -> float:
... print(a, b)
...
>>> f(88)
88 None
>>> f.__annotations__
{'a': 99, 'b': 'spam', 'return': <class 'float'>}

Quelle: Python Pocket Reference, 5. Ausgabe

BEISPIEL:

Das typeannotations Modul bietet eine Reihe von Tools zur Typprüfung und Typinferenz von Python-Code. Es enthält auch eine Reihe von Typen, die zum Kommentieren von Funktionen und Objekten nützlich sind.

Diese Tools sind hauptsächlich für statische Analysegeräte wie Linters, Code-Vervollständigungsbibliotheken und IDEs konzipiert. Zusätzlich werden Dekorateure zur Durchführung von Laufzeitprüfungen bereitgestellt. Die Überprüfung des Laufzeit-Typs ist in Python nicht immer eine gute Idee, kann jedoch in einigen Fällen sehr nützlich sein.

https://github.com/ceronman/typeannotations

Wie das Schreiben hilft, besseren Code zu schreiben

Durch Eingabe können Sie statische Code-Analysen durchführen, um Typfehler zu erkennen, bevor Sie Ihren Code an die Produktion senden, und um offensichtliche Fehler zu vermeiden. Es gibt Tools wie mypy, die Sie im Rahmen Ihres Software-Lebenszyklus zu Ihrer Toolbox hinzufügen können. mypy kann nach korrekten Typen suchen, indem es teilweise oder vollständig gegen Ihre Codebasis ausgeführt wird. Mit mypy können Sie auch Fehler erkennen, z. B. nach dem Typ None suchen, wenn der Wert von einer Funktion zurückgegeben wird. Durch Eingabe können Sie Ihren Code sauberer machen. Anstatt Ihren Code mithilfe von Kommentaren zu dokumentieren, in denen Sie Typen in einer Dokumentzeichenfolge angeben, können Sie Typen ohne Leistungskosten verwenden.

Clean Python: Elegante Codierung in Python ISBN: ISBN-13 (pbk): 978-1-4842-4877-5

PEP 526 - Syntax für variable Anmerkungen

https://www.python.org/dev/peps/pep-0526/

https://www.attrs.org/en/stable/types.html


@ BlackJack, war das "zur Verwendung in Erweiterungen" nicht klar?
Der Demz

Es ist klar, beantwortet aber IMHO die Frage nicht. Es ist so, als würde man „Was sind gute Verwendungszwecke von Klassen?“ Mit „Zur Verwendung in Programmen“ beantworten. Es ist klar, richtig, aber die fragende Partei ist nicht wirklich klüger, was zum Teufel gute konkrete Verwendungszwecke sind. Ihre Antwort ist nicht allgemeiner. Ein Beispiel entspricht im Wesentlichen dem bereits in der Frage enthaltenen .
BlackJack

1

Trotz aller hier beschriebenen Verwendungen wird die durchsetzbare und höchstwahrscheinlich erzwungene Verwendung von Anmerkungen für Typhinweise verwendet .

Dies wird derzeit in keiner Weise erzwungen, aber nach PEP 484 lassen zukünftige Versionen von Python nur Typen als Wert für Anmerkungen zu.

Zitieren Was ist mit vorhandenen Verwendungen von Anmerkungen? ::

Wir hoffen, dass Typhinweise irgendwann die einzige Verwendung für Anmerkungen sein werden. Dies erfordert jedoch zusätzliche Diskussionen und eine Verfallsfrist nach der ersten Einführung des Typisierungsmoduls mit Python 3.5. Das aktuelle PEP hat den vorläufigen Status (siehe PEP 411), bis Python 3.6 veröffentlicht wird. Das am schnellsten denkbare Schema würde die stille Ablehnung von Annotationen ohne Typhinweise in 3.6 und die vollständige Ablehnung von Annotationen ohne Typ in 3.7 einführen und Typhinweise als die einzig zulässige Verwendung von Annotationen in Python 3.8 deklarieren.

Obwohl ich in 3.6 noch keine stillen Abwertungen gesehen habe, könnte dies stattdessen auf 3.7 erhöht werden.

Auch wenn es einige andere gute Anwendungsfälle gibt, ist es am besten, sie nur als Typhinweise zu verwenden, wenn Sie in Zukunft nicht alles ändern möchten, wo diese Einschränkung besteht.


1

Als etwas verzögerte Antwort verwenden einige meiner Pakete (marrow.script, WebCore usw.), sofern verfügbar, Anmerkungen, um die Typumwandlung zu deklarieren (dh eingehende Werte aus dem Web zu transformieren, zu erkennen, welche Argumente boolesche Schalter sind usw.) um ein zusätzliches Markup von Argumenten durchzuführen.

Marrow Script erstellt eine vollständige Befehlszeilenschnittstelle für beliebige Funktionen und Klassen und ermöglicht das Definieren von Dokumentations-, Casting- und Rückruf-abgeleiteten Standardwerten über Anmerkungen mit einem Dekorator, der ältere Laufzeiten unterstützt. Alle meine Bibliotheken, die Anmerkungen verwenden, unterstützen die Formulare:

any_string  # documentation
any_callable  # typecast / callback, not called if defaulting
(any_callable, any_string)  # combination
AnnotationClass()  # package-specific rich annotation object
[AnnotationClass(), AnnotationClass(), …]  # cooperative annotation

Die "Bare" -Unterstützung für Docstrings oder Typecasting-Funktionen ermöglicht ein einfacheres Mischen mit anderen Bibliotheken, die Anmerkungen berücksichtigen. (Dh ich habe einen Web-Controller, der Typecasting verwendet und zufällig auch als Befehlszeilenskript verfügbar gemacht wird.)

Bearbeitet, um hinzuzufügen: Ich habe auch damit begonnen, das TypeGuard- Paket mithilfe von Zusicherungen zur Entwicklungszeit zur Validierung zu verwenden. Vorteil: Bei Ausführung mit aktivierten "Optimierungen" ( -O/ PYTHONOPTIMIZEenv var) werden die möglicherweise teuren (z. B. rekursiven) Überprüfungen weggelassen, mit der Idee, dass Sie Ihre App in der Entwicklung ordnungsgemäß getestet haben, sodass die Überprüfungen in der Produktion unnötig sein sollten.


-2

Anmerkungen können zum einfachen Modularisieren von Code verwendet werden. Zum Beispiel könnte ein Modul für ein Programm, das ich pflege, einfach eine Methode definieren wie:

def run(param1: int):
    """
    Does things.

    :param param1: Needed for counting.
    """
    pass

und wir könnten den Benutzer nach einem Ding namens "param1" fragen, das "zum Zählen benötigt" ist und ein "int" sein sollte. Am Ende können wir sogar die vom Benutzer angegebene Zeichenfolge in den gewünschten Typ konvertieren, um die problemloseste Erfahrung zu erzielen.

In unserem Funktionsmetadatenobjekt finden Sie eine Open Source-Klasse, die dabei hilft und die benötigten Werte automatisch abrufen und in einen beliebigen Typ konvertieren kann (da die Annotation eine Konvertierungsmethode ist). Sogar IDEs zeigen Autocompletions richtig und gehen davon aus, dass die Typen den Anmerkungen entsprechen - eine perfekte Anpassung.


-2

Wenn Sie sich die Liste der Vorteile von Cython ansehen, ist die Fähigkeit, dem Compiler mitzuteilen, welcher Typ ein Python-Objekt ist, eine wichtige.

Ich kann mir eine Zukunft vorstellen, in der Cython (oder ähnliche Tools, die einen Teil Ihres Python-Codes kompilieren) die Annotationssyntax verwenden wird, um ihre Magie zu entfalten.


Der RPython Annotator ist ein Beispiel für einen Ansatz, der sich angemessen pythonisch anfühlt. Nachdem Sie ein Diagramm Ihrer Anwendung erstellt haben, kann es den Typ jeder Variablen ermitteln und (für RPython) die Sicherheit eines einzelnen Typs erzwingen. OTOH erfordert "Boxen" oder andere Lösungen / Workarounds, um dynamische Rich-Werte zu ermöglichen. Wer bin ich, um meine multiplyFunktion zu zwingen , nur gegen ganze Zahlen zu arbeiten, wenn dies 'na' * 8 + ' batman!'vollständig gültig ist? ;)
amcgregor
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.