Python có bị gõ yếu không?

Gõ yếu có nghĩa là cho phép truy cập vào biểu diễn cơ bản. Trong C, tôi có thể tạo một con trỏ tới các ký tự, sau đó nói với trình biên dịch rằng tôi muốn sử dụng nó làm con trỏ tới các số nguyên

char sz[] = "abcdefg";
int *i = (int *)sz;

Trên nền tảng little-endian với các số nguyên 32 bit, điều này biến

intptr_t i = (intptr_t)&sz;
6 thành một mảng gồm các số
intptr_t i = (intptr_t)&sz;
7 và
intptr_t i = (intptr_t)&sz;
8. Trên thực tế, bạn thậm chí có thể tự truyền con trỏ tới số nguyên (có kích thước phù hợp)

intptr_t i = (intptr_t)&sz;

Và tất nhiên điều này có nghĩa là tôi có thể ghi đè lên bộ nhớ ở bất kỳ đâu trong hệ thống. *

char *spam = (char *)0x12345678
spam[0] = 0;

* Tất nhiên, hệ điều hành hiện đại sử dụng bộ nhớ ảo và bảo vệ trang nên tôi chỉ có thể ghi đè lên bộ nhớ của tiến trình của riêng mình, nhưng bản thân C không có gì cung cấp khả năng bảo vệ như vậy, như bất kỳ ai từng viết mã trên Mac OS cổ điển hoặc Win16 đều có thể cho bạn biết

Lisp truyền thống cho phép các loại tin tặc tương tự;

Hầu hết các ngôn ngữ ngày nay không yếu như C và Lisp, nhưng nhiều ngôn ngữ trong số chúng vẫn hơi bị rò rỉ. Ví dụ: bất kỳ ngôn ngữ OO nào có "downcast" không được chọn,* đó là rò rỉ kiểu. về cơ bản, bạn đang nói với trình biên dịch "Tôi biết tôi đã không cung cấp cho bạn đủ thông tin để biết điều này là an toàn, nhưng tôi khá chắc chắn là như vậy," khi toàn bộ điểm của một hệ thống kiểu là trình biên dịch luôn có đủ thông tin

* Một downcast được kiểm tra không làm cho hệ thống loại của ngôn ngữ yếu hơn chỉ vì nó chuyển kiểm tra sang thời gian chạy. Nếu đúng như vậy, thì tính đa hình của kiểu con (còn gọi là các lệnh gọi hàm ảo hoặc hoàn toàn động) sẽ vi phạm tương tự đối với hệ thống kiểu và tôi không nghĩ có ai muốn nói điều đó

Rất ít ngôn ngữ "kịch bản" yếu theo nghĩa này. Ngay cả trong Perl hoặc Tcl, bạn không thể lấy một chuỗi và chỉ diễn giải các byte của nó dưới dạng số nguyên. * Nhưng điều đáng chú ý là trong CPython (và tương tự như vậy đối với nhiều trình thông dịch khác cho nhiều ngôn ngữ), nếu bạn thực sự kiên trì, bạn có thể sử dụng

intptr_t i = (intptr_t)&sz;
9 để tải lên
char *spam = (char *)0x12345678
spam[0] = 0;
0, chuyển _____21 của đối tượng sang _____22 và buộc hệ thống loại phải . Điều này có làm cho hệ thống loại yếu đi hay không tùy thuộc vào các trường hợp sử dụng của bạn—nếu bạn đang cố gắng triển khai hộp cát thực thi bị hạn chế bằng ngôn ngữ để đảm bảo an ninh, thì bạn phải đối phó với những kiểu thoát này…

* Bạn có thể sử dụng một hàm như

intptr_t i = (intptr_t)&sz;
0 để đọc các byte và tạo một int mới từ "cách C biểu thị các byte này", nhưng điều đó rõ ràng là không bị rò rỉ;


Trong khi đó, chuyển đổi ẩn thực sự là một điều khác với hệ thống kiểu yếu hoặc rò rỉ

Mọi ngôn ngữ, kể cả Haskell, đều có các hàm để chuyển đổi một số nguyên thành một chuỗi hoặc một số float. Nhưng một số ngôn ngữ sẽ tự động thực hiện một số chuyển đổi đó cho bạn—e. g. , trong C, nếu bạn gọi một hàm muốn có một

intptr_t i = (intptr_t)&sz;
1, và bạn chuyển nó vào
intptr_t i = (intptr_t)&sz;
2, thì nó sẽ được chuyển đổi cho bạn. Điều này chắc chắn có thể dẫn đến lỗi với, e. g. , lỗi tràn không mong muốn, nhưng chúng không phải là loại lỗi giống như bạn gặp phải từ hệ thống loại yếu. Và C không thực sự yếu hơn ở đây;

Và với các ngôn ngữ động, điều này khá âm u. Không có thứ gọi là "hàm muốn nổi" trong Python hoặc Perl. Nhưng có những chức năng quá tải thực hiện những việc khác nhau với các loại khác nhau và có một cảm giác trực quan mạnh mẽ rằng, e. g. , thêm một chuỗi vào một thứ khác là "một hàm muốn có một chuỗi". Theo nghĩa đó, Perl, Tcl và JavaScript dường như thực hiện rất nhiều chuyển đổi ngầm định (

intptr_t i = (intptr_t)&sz;
3 mang lại cho bạn
intptr_t i = (intptr_t)&sz;
4), trong khi Python thực hiện ít hơn nhiều (
intptr_t i = (intptr_t)&sz;
3 đưa ra một ngoại lệ, nhưng
intptr_t i = (intptr_t)&sz;
6 mang lại cho bạn
intptr_t i = (intptr_t)&sz;
7*). Thật khó để diễn đạt ý nghĩa đó thành các thuật ngữ chính thức—tại sao không nên có một
intptr_t i = (intptr_t)&sz;
8 nhận một chuỗi và một số nguyên, trong khi rõ ràng có các chức năng khác, chẳng hạn như lập chỉ mục, thực hiện điều đó?

* Trên thực tế, trong Python hiện đại, điều đó có thể được giải thích theo kiểu con OO, vì

intptr_t i = (intptr_t)&sz;
9 là đúng. Tôi không nghĩ rằng có bất kỳ ý nghĩa nào trong đó
char *spam = (char *)0x12345678
spam[0] = 0;
0 là một thể hiện của kiểu chuỗi trong Perl hoặc JavaScript… mặc dù trong Tcl, nó thực sự là như vậy, vì mọi thứ đều là một thể hiện của chuỗi


Cuối cùng, có một định nghĩa khác, hoàn toàn trực giao, về "mạnh mẽ" so với. gõ "yếu", trong đó "mạnh" có nghĩa là mạnh mẽ/linh hoạt/biểu cảm

Ví dụ: Haskell cho phép bạn xác định loại là số, chuỗi, danh sách loại này hoặc ánh xạ từ chuỗi sang loại này, đây là cách hoàn hảo để biểu thị mọi thứ có thể được giải mã từ JSON. Không có cách nào để xác định loại như vậy trong Java. Nhưng ít nhất Java có các kiểu tham số (chung), vì vậy bạn có thể viết một hàm lấy Danh sách T và biết rằng các phần tử thuộc kiểu T; . Nhưng ít nhất Java cho phép bạn tạo các kiểu mới với các phương thức riêng của chúng; . Và BCPL thậm chí không có điều đó. Và tiếp tục cho đến lắp ráp, trong đó các loại duy nhất có độ dài bit khác nhau

Vì vậy, theo nghĩa đó, hệ thống kiểu của Haskell mạnh hơn Java hiện đại, mạnh hơn Java trước đó, mạnh hơn C, mạnh hơn BCPL

Vì vậy, Python phù hợp với phạm vi đó ở đâu? . Trong nhiều trường hợp, cách gõ vịt cho phép bạn mô phỏng mọi thứ bạn có thể làm trong Haskell và thậm chí một số thứ bạn không thể làm được; . Tuy nhiên, có những trường hợp gõ vịt là không đủ. Ví dụ, trong Haskell, bạn có thể nói rằng một danh sách int trống là một danh sách int, vì vậy bạn có thể quyết định rằng việc giảm

intptr_t i = (intptr_t)&sz;
8 trên danh sách đó sẽ trả về 0*;

* Trên thực tế, Haskell không cho phép bạn làm điều này; . Nhưng hệ thống loại của nó đủ mạnh để bạn có thể thực hiện công việc này và Python thì không