Python jest silnie, dynamicznie pisany.
- Silne pisanie oznacza, że typ wartości nie zmienia się w nieoczekiwany sposób. Ciąg zawierający tylko cyfry nie magicznie staje się liczbą, co może się zdarzyć w Perlu. Każda zmiana typu wymaga wyraźnej konwersji.
- Typowanie dynamiczne oznacza, że obiekty wykonawcze (wartości) mają typ, w przeciwieństwie do typowania statycznego, w którym zmienne mają typ.
Jak na twój przykład
bob = 1
bob = "bob"
Działa to, ponieważ zmienna nie ma typu; może nazwać dowolny obiekt. Po tym bob=1
zobaczysz, że type(bob)
powraca int
, ale potem bob="bob"
powraca str
. (Zauważ, że type
jest to funkcja regularna, więc ocenia swój argument, a następnie zwraca typ wartości).
Porównaj to ze starszymi dialektami języka C, które były słabo wpisane statycznie, dzięki czemu wskaźniki i liczby całkowite były prawie wymienne. (Współczesny ISO C wymaga konwersji w wielu przypadkach, ale mój kompilator nadal domyślnie nie toleruje tego).
Muszę dodać, że pisanie mocne kontra słabe jest raczej kontinuum niż wyborem logicznym. C ++ ma silniejsze pisanie niż C (wymagane jest więcej konwersji), ale system typów można obalić za pomocą rzutowania wskaźnika.
O sile systemu typów w dynamicznym języku, takim jak Python, w rzeczywistości decyduje sposób, w jaki jego prymitywy i funkcje biblioteczne reagują na różne typy. Np. +
Jest przeciążony, więc działa na dwóch liczbach lub dwóch ciągach znaków, ale nie na ciągu znaków i liczbie. Jest to wybór projektowy dokonany po +
jego wdrożeniu, ale tak naprawdę nie jest to konieczność wynikająca z semantyki języka. W rzeczywistości, gdy przeciążasz +
niestandardowy typ, możesz niejawnie przekonwertować wszystko na liczbę:
def to_number(x):
"""Try to convert function argument to float-type object."""
try:
return float(x)
except (TypeError, ValueError):
return 0
class Foo:
def __init__(self, number):
self.number = number
def __add__(self, other):
return self.number + to_number(other)
Instancję klasy Foo
można dodać do innych obiektów:
>>> a = Foo(42)
>>> a + "1"
43.0
>>> a + Foo
42
>>> a + 1
43.0
>>> a + None
42
Zauważmy, że chociaż silnie wpisane Python jest całkowicie w porządku z dodawania obiektów typu int
i float
i zwraca obiekt typu float
(np int(42) + float(1)
zwrotów 43.0
). Z drugiej strony, ze względu na niedopasowanie między typami Haskell będzie skarżyć, jeśli ktoś próbuje co następuje (42 :: Integer) + (1 :: Float)
. To sprawia, że Haskell jest językiem ściśle typowym, w którym typy są całkowicie rozłączne i możliwe jest jedynie kontrolowane przeładowanie za pomocą klas typów.
True
lubFalse
. Ale co z promocją liczb?1.0 + 2
działa równie dobrze w Pythonie, jak w Perlu lub C, nawet jeśli"1.0" + 2
nie. Zgadzam się z @jbrendel, że to nie jest tak naprawdę domniemana konwersja, to tylko przeciążenie - ale w tym samym sensie Perl również nie dokonuje żadnej niejawnej konwersji. Jeśli funkcje nie mają zadeklarowanych typów parametrów, nie ma mowy o domniemanej konwersji.if isValid(value) - 1
wyciek może. Wartość logiczna jest wymuszana na liczbę całkowitą, która jest następnie oceniana jako prawdziwa wartość.False - 1
staje się prawdomówny iTrue - 1
staje się fałszem, co prowadzi do krępująco trudnego, dwuwarstwowego błędu debugowania. W tym sensie python jest najczęściej silnie pisany; koercje typu zwykle nie powodują błędów logicznych.Są pewne ważne kwestie, które moim zdaniem przeoczyły wszystkie istniejące odpowiedzi.
Słabe pisanie oznacza umożliwienie dostępu do podstawowej reprezentacji. W C mogę utworzyć wskaźnik do znaków, a następnie powiedzieć kompilatorowi, że chcę go użyć jako wskaźnika do liczb całkowitych:
Na platformie little-endian z 32-bitowymi liczbami całkowitymi tworzy
i
to tablicę liczb0x64636261
i0x00676665
. W rzeczywistości możesz nawet rzutować same wskaźniki na liczby całkowite (o odpowiednim rozmiarze):I oczywiście oznacza to, że mogę nadpisać pamięć w dowolnym miejscu w systemie. *
* Oczywiście współczesny system operacyjny korzysta z pamięci wirtualnej i ochrony strony, więc mogę tylko nadpisać pamięć własnego procesu, ale nie ma nic w samym C, które oferuje taką ochronę, jak każdy, kto kiedykolwiek napisał kod, na przykład Classic Mac OS lub Win16.
Tradycyjne Lisp pozwalało na podobne hakowanie; na niektórych platformach liczby zmiennoprzecinkowe i minusowe komórki były tego samego typu, i można było po prostu przekazać jedną funkcję, oczekując drugiej, i „zadziałałaby”.
Obecnie większość języków nie jest tak słaba, jak C i Lisp, ale wiele z nich wciąż jest nieszczelnych. Na przykład, każdy język OO, który ma odznaczony „downcast”, * to przeciek typu: w zasadzie mówisz kompilatorowi „Wiem, że nie podałem ci wystarczającej ilości informacji, aby wiedzieć, że jest to bezpieczne, ale jestem całkiem pewien tak jest, „gdy cały punkt systemu typów polega na tym, że kompilator zawsze ma wystarczającą ilość informacji, aby wiedzieć, co jest bezpieczne.
* Sprawdzony downcast nie osłabia systemu typów języka tylko dlatego, że przenosi czek do środowiska wykonawczego. Gdyby tak było, wówczas polimorfizm podtypów (inaczej wirtualne lub w pełni dynamiczne wywołania funkcji) stanowiłby to samo naruszenie systemu typów i nie sądzę, aby ktokolwiek chciał to powiedzieć.
Bardzo niewiele języków „skryptowych” jest słabych w tym sensie. Nawet w Perlu lub Tcl nie można pobrać łańcucha i po prostu zinterpretować jego bajty jako liczbę całkowitą. * Warto jednak zauważyć, że w CPython (i podobnie w przypadku wielu innych tłumaczy dla wielu języków), jeśli jesteś naprawdę wytrwały, można użyć
ctypes
załadowaćlibpython
, rzutować obiektuid
doPOINTER(Py_Object)
i zmusić system typu przeciekać. To, czy powoduje to osłabienie systemu typów, zależy od przypadków użycia - jeśli próbujesz zaimplementować piaskownicę z ograniczeniami wykonania w języku w celu zapewnienia bezpieczeństwa, musisz poradzić sobie z tego rodzaju ucieczkami…* Możesz użyć funkcji takiej jak
struct.unpack
odczyt bajtów i zbudowanie nowej int z „jak C reprezentowałby te bajty”, ale to oczywiście nie jest nieszczelne; nawet Haskell na to pozwala.Tymczasem niejawna konwersja naprawdę różni się od słabego lub nieszczelnego systemu typu.
Każdy język, nawet Haskell, ma funkcje, powiedzmy, konwersji liczby całkowitej na ciąg lub liczbę zmiennoprzecinkową. Ale niektóre języki wykonują dla Ciebie niektóre z tych konwersji automatycznie - np. W C, jeśli wywołasz funkcję, która chce
float
, a przekażesz jąint
, zostanie ona dla Ciebie przekonwertowana. To z pewnością może prowadzić do błędów, np. Z nieoczekiwanymi przepełnieniami, ale nie są to te same rodzaje błędów, które otrzymujesz z systemu słabego typu. A C tak naprawdę nie jest tutaj wcale słabszy; możesz dodać liczbę całkowitą i liczbę zmiennoprzecinkową w Haskell lub nawet połączyć zmiennoprzecinkową z łańcuchem, po prostu musisz to zrobić wyraźniej.A w przypadku dynamicznych języków jest to dość mętne. W Pythonie i Perlu nie ma czegoś takiego jak „funkcja, która chce liczby zmiennoprzecinkowej”. Ale są przeciążone funkcje, które robią różne rzeczy z różnymi typami, i istnieje silne intuicyjne poczucie, że np. Dodanie łańcucha do czegoś innego jest „funkcją, która chce łańcucha”. W tym sensie Perl, Tcl i JavaScript wydają się robić wiele niejawnych konwersji (
"a" + 1
daje ci"a1"
), podczas gdy Python robi o wiele mniej ("a" + 1
podnosi wyjątek, ale1.0 + 1
daje2.0
*). Po prostu trudno jest sformułować ten sens w kategoriach formalnych - dlaczego nie powinien istnieć ciąg,+
który bierze ciąg i liczbę całkowitą, skoro oczywiście istnieją inne funkcje, takie jak indeksowanie?* W rzeczywistości we współczesnym Pythonie można to wytłumaczyć podtypami OO, ponieważ
isinstance(2, numbers.Real)
jest to prawda. Nie wydaje mi się, żeby istniała jakaś2
instancja typu łańcucha w Perlu lub JavaScript… chociaż tak naprawdę jest w Tcl, ponieważ wszystko jest instancją łańcucha.Wreszcie istnieje inna, całkowicie ortogonalna, definicja „silnego” vs. „słabego” pisania, gdzie „mocny” oznacza mocny / elastyczny / ekspresyjny.
Na przykład Haskell pozwala zdefiniować typ, który jest liczbą, łańcuchem, listą tego typu lub mapą ciągów znaków na ten typ, co jest doskonałym sposobem na przedstawienie wszystkiego, co można zdekodować z JSON. Nie ma możliwości zdefiniowania takiego typu w Javie. Ale przynajmniej Java ma typy parametryczne (ogólne), więc możesz napisać funkcję, która pobiera Listę T i wie, że elementy są typu T; inne języki, takie jak wczesna Java, zmusiły cię do użycia Listy Obiektów i spuszczenia. Ale przynajmniej Java pozwala tworzyć nowe typy własnymi metodami; C pozwala tylko tworzyć struktury. I BCPL nawet tego nie miał. I tak dalej, aż do montażu, gdzie jedynymi typami są różne długości bitów.
W tym sensie system typów Haskella jest silniejszy niż współczesny Java, który jest silniejszy niż wcześniejszy Java, który jest silniejszy niż C, który jest silniejszy niż BCPL.
Gdzie więc Python pasuje do tego spektrum? To trochę trudne. W wielu przypadkach pisanie kaczek pozwala symulować wszystko, co możesz zrobić w Haskell, a nawet niektóre rzeczy, których nie możesz; Oczywiście błędy są wychwytywane w czasie wykonywania zamiast czasu kompilacji, ale nadal są wychwytywane. Są jednak przypadki, w których pisanie kaczek nie jest wystarczające. Na przykład w Haskell możesz powiedzieć, że pusta lista liczb całkowitych jest listą liczb wewnętrznych, więc możesz zdecydować, że zmniejszenie
+
tej listy powinno zwrócić 0 *; w Pythonie pusta lista jest pustą listą; nie ma informacji o typie, które pomogłyby ci zdecydować, co+
powinno zrobić redukcja .* W rzeczywistości Haskell nie pozwala ci tego zrobić; jeśli wywołasz funkcję zmniejszania, która nie przyjmuje wartości początkowej na pustej liście, pojawi się błąd. Ale jej system typ jest wystarczająco silny, aby mógł dokonać tej pracy, a Python nie jest.
źródło
char sz[]
nie jest wskaźnikiem do char, jest tablicą char, aw zadaniu rozkłada się we wskaźnik.Mylisz „silnie pisane” z „dynamicznie pisane” .
Nie mogę zmienić typu
1
, dodając ciąg'12'
, ale mogę wybrać, jakie typy przechowuję w zmiennej i zmienić to w czasie wykonywania programu.Przeciwieństwem pisania dynamicznego jest pisanie statyczne; deklaracja typów zmiennych nie zmienia się w trakcie trwania programu. Przeciwieństwem silnego pisania jest słabe pisanie; rodzaj wartości może ulec zmianie w trakcie trwania programu.
źródło
Według tej wiki artykuł w Pythonie Python jest dynamicznie i silnie pisany (zapewnia również dobre wyjaśnienie).
Być może myślisz o statycznie typowanych językach, w których typy nie mogą się zmieniać podczas wykonywania programu, a sprawdzanie typu następuje w czasie kompilacji w celu wykrycia ewentualnych błędów.
To pytanie SO może być interesujące: Języki typu dynamicznego kontra języki typu statycznego i ten artykuł w Wikipedii na temat systemów typu zawiera więcej informacji
źródło
TLDR;
Typowanie w Pythonie jest dynamiczne, więc możesz zmienić zmienną łańcuchową na int
Pisanie w Pythonie jest silne, więc nie można scalać typów:
W słabo wpisanym Javascriptie dzieje się tak ...
Odnośnie wnioskowania typu
Java zmusza cię do jawnego zadeklarowania typów obiektów
Kotlin używa wnioskowania, aby zdać sobie sprawę, że to jest
int
Ponieważ oba języki używają typów statycznych ,
x
nie można ich zmienić zint
. Żaden język nie pozwoliłby na taką dynamiczną zmianęźródło
'x' + 3
może to byćoperator+
przeciążenie i konwersja typu za sceną?Odpowiedzi na to już kilka razy, ale Python jest silnie napisanym językiem:
W JavaScript:
To jest różnica między słabym i mocnym pisaniem. Słabe typy automatycznie próbują konwertować z jednego typu na inny, w zależności od kontekstu (np. Perl). Silne typy nigdy nie dokonują konwersji pośrednio.
Twoje zamieszanie polega na niezrozumieniu, w jaki sposób Python wiąże wartości z nazwami (powszechnie nazywanymi zmiennymi).
W Pythonie nazwy nie mają typów, więc możesz robić takie rzeczy jak:
I imiona można przypisać do dowolnego:
Do dalszego czytania:
https://en.wikipedia.org/wiki/Dynamic_dispatch
i nieco powiązane, ale bardziej zaawansowane:
http://effbot.org/zone/call-by-object.htm
źródło
"3"*4
w Pythonie. Rezultatem jest oczywiście"3333"
. Nie powiedziałbyś, że przekształca którąkolwiek z tych rzeczy. Oczywiście może to być tylko argumentacja semantyki.float
z kombinacjifloat
iint
że domyślnie konwertuje typ. Istnieje naturalna zależność między zmiennoprzecinkową i wewnętrzną, a tak naprawdę, typowa heirarchia to określa. Przypuszczam, że można argumentować, że Javascript uważa'3'+4
i'e'+4
oba są dobrze zdefiniowanymi operacjami w taki sam sposób, jak Python uważa3.0 + 4
za dobrze zdefiniowane, ale w tym momencie tak naprawdę nie ma czegoś takiego jak silne lub słabe typy, tylko (nie) zdefiniowane operacje.Zmienna Python przechowuje nietypowe odwołanie do obiektu docelowego, który reprezentuje wartość.
Każda operacja przypisania oznacza przypisanie nietypowego odwołania do przypisanego obiektu - tj. Obiekt jest współdzielony przez oryginalne i nowe (policzone) odniesienia.
Typ wartości jest powiązany z obiektem docelowym, a nie z wartością odniesienia. (Silne) sprawdzanie typu jest wykonywane, gdy wykonywana jest operacja z wartością (czas działania).
Innymi słowy, zmienne (technicznie) nie mają typu - nie ma sensu myśleć w kategoriach typu zmiennego, jeśli chce się być dokładnym. Ale odniesienia są automatycznie usuwane z odniesień i faktycznie myślimy w kategoriach typu obiektu docelowego.
źródło
Termin „silne pisanie na klawiaturze” nie ma określonej definicji.
Dlatego użycie tego terminu zależy od tego, z kim rozmawiasz.
Nie uważam żadnego języka, w którym typ zmiennej nie jest albo jawnie zadeklarowany, ani statycznie wpisany, aby był silnie typowany.
Silne pisanie nie tylko wyklucza konwersję (na przykład „automatyczne” konwertowanie z liczby całkowitej na ciąg). Wyklucza to przypisanie (tj. Zmianę typu zmiennej).
Jeśli poniższy kod się kompiluje (interpretuje), język nie jest pisany silnym typem:
Foo = 1 Foo = „1”
W silnie napisanym języku programista może „liczyć” na typ.
Na przykład, jeśli programista zobaczy deklarację,
UINT64 kZarkCount;
i on lub ona wie, że 20 linii później, kZarkCount jest nadal UINT64 (o ile występuje w tym samym bloku) - bez konieczności sprawdzania interweniującego kodu.
źródło
Właśnie odkryłem świetny zwięzły sposób na zapamiętanie go:
źródło
Myślę, że ten prosty przykład powinien wyjaśnić różnice między silnym i dynamicznym pisaniem:
Jawa:
źródło
Powyższe spowodowałoby koszmar niemożliwego do utrzymania kodu w dużym systemie przez długi okres czasu. Nazwij to jak chcesz, ale możliwość „dynamicznej” zmiany typu zmiennych to po prostu zły pomysł ...
źródło