Python to mój pierwszy dynamiczny język. Niedawno zakodowałem wywołanie funkcji, które niepoprawnie podaje nieprawidłową liczbę argumentów. Nie udało się to z wyjątkiem w momencie wywołania tej funkcji. Spodziewałem się, że nawet w języku dynamicznym tego rodzaju błąd można wykryć podczas analizowania pliku źródłowego.
Rozumiem, że typ rzeczywistych argumentów nie jest znany do momentu wywołania funkcji, ponieważ ta sama zmienna może zawierać wartości dowolnego typu w różnym czasie. Ale liczba argumentów jest znana, gdy tylko plik źródłowy jest analizowany. Nie zmieni się podczas działania programu.
Więc to nie jest kwestia filozoficzna
Aby zachować to w zakresie Stack Overflow, pozwólcie, że sformułuję pytanie w ten sposób. Czy jest jakaś funkcja oferowana przez Python, która wymaga od niego opóźnienia sprawdzenia liczby argumentów w wywołaniu funkcji do czasu rzeczywistego wykonania kodu?
f(*args)
nie będzie znana podczas fazy analizowania.def foo(): whatever
mafoo.__name__ == 'foo'
, ale to nie powstrzyma Cię przed wykonaniemfoo = some_other_function
lubbar = foo
.Odpowiedzi:
Python nie może z góry wiedzieć, jaki obiekt zostanie wywołany, ponieważ będąc dynamicznym, możesz zamienić obiekt funkcji . Kiedykolwiek. Każdy z tych obiektów może mieć inną liczbę argumentów.
Oto skrajny przykład:
import random def foo(): pass def bar(arg1): pass def baz(arg1, arg2): pass the_function = random.choice([foo, bar, baz]) print(the_function())
Powyższy kod ma 2 na 3 szanse na zgłoszenie wyjątku. Ale Python nie może wiedzieć a-priori, czy tak będzie, czy nie!
I nawet nie zacząłem od dynamicznego importu modułów, dynamicznego generowania funkcji, innych wywoływalnych obiektów (
__call__
można wywołać dowolny obiekt z metodą) lub argumentów typu catch-all (*args
i**kwargs
).Aby jednak było to bardziej jasne, w swoim pytaniu stawiasz:
Tak nie jest, nie w Pythonie, po załadowaniu modułu możesz usunąć, dodać lub zamienić dowolny obiekt w przestrzeni nazw modułu, w tym obiekty funkcji.
źródło
*
argumentów… a to jest Python, więc… tak, dość ekstremalne.https://stackoverflow.com/a/34567789
i skończyć z tym.Znana jest liczba przekazywanych argumentów, ale nie jest to faktycznie wywołana funkcja. Zobacz ten przykład:
def foo(): print("I take no arguments.") def bar(): print("I call foo") foo()
Może się to wydawać oczywiste, ale umieśćmy je w pliku o nazwie „fubar.py”. Teraz w interaktywnej sesji Pythona wykonaj następujące czynności:
>>> import fubar >>> fubar.foo() I take no arguments. >>> fubar.bar() I call foo I take no arguments.
To było oczywiste. A teraz zabawna część. Zdefiniujemy funkcję, która wymaga niezerowej ilości argumentów:
>>> def notfoo(a): ... print("I take arguments!") ...
Teraz robimy coś, co nazywa się małpą łataniem . Możemy faktycznie zastąpić funkcję
foo
wfubar
module:>>> fubar.foo = notfoo
Teraz, kiedy wołamy
bar
,TypeError
zostanie podniesiona wola; nazwafoo
odnosi się teraz do funkcji, którą zdefiniowaliśmy powyżej, zamiast do pierwotnej funkcji znanej wcześniej jakofoo
.>>> fubar.bar() I call foo Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/home/horazont/tmp/fubar.py", line 6, in bar foo() TypeError: notfoo() missing 1 required positional argument: 'a'
Więc nawet w takiej sytuacji, w której może wydawać się bardzo oczywiste, że wywoływana funkcja
foo
nie przyjmuje żadnych argumentów, Python może wiedzieć, że jest to faktyczniefoo
funkcja, która jest wywoływana, gdy wykonuje tę linię źródłową.Jest to właściwość Pythona, która sprawia, że jest on potężny, ale powoduje też pewne powolne działanie. W rzeczywistości, uczynienie modułów tylko do odczytu w celu poprawy wydajności było omawiane na liście mailingowej python-ideas jakiś czas temu, ale nie zyskało żadnego realnego wsparcia.
źródło