Hướng dẫn python uses duck typing - python sử dụng tiếng gõ vịt

Trong bài học này, bạn sẽ tìm hiểu về gõ vịt. Thuật ngữ này xuất phát từ câu nói nếu nó đi bộ như một con vịt, và nó bị xáo trộn như một con vịt, thì nó phải là một con vịt. (Có các biến thể khác).duck typing. This term comes from the saying “If it walks like a duck, and it quacks like a duck, then it must be a duck.” (There are other variations).

Gõ vịt là một khái niệm liên quan đến gõ động, trong đó loại hoặc lớp của một đối tượng ít quan trọng hơn các phương pháp mà nó định nghĩa. Khi bạn sử dụng gõ vịt, bạn hoàn toàn không kiểm tra các loại. Thay vào đó, bạn kiểm tra sự hiện diện của một phương thức hoặc thuộc tính nhất định.dynamic typing, where the type or the class of an object is less important than the methods it defines. When you use duck typing, you do not check types at all. Instead, you check for the presence of a given method or attribute.

Ví dụ: bạn có thể gọi len() trên bất kỳ đối tượng Python nào xác định phương thức .__len__():

>>>

>>> class TheHobbit:
...     def __len__(self):
...         return 95022
...
...
>>> the_hobbit = TheHobbit()

>>> the_hobbit
<__main__.TheHobbit object at 0x108deeef0>

>>> len(the_hobbit)
95022

>>> my_str = "Hello World"
>>> my_list = [34, 54, 65, 78]
>>> my_dict = {"one": 123, "two": 456, "three": 789}

>>> len(my_str)
11
>>> len(my_list)
4
>>> len(my_dict)
3
>>> len(the_hobbit)
95022

>>> my_int = 7
>>> my_float = 42.3

>>> len(my_int)
Traceback (most recent call last):
  File "", line 1, in 
    len(my_int)
TypeError: object of type 'int' has no len()

>>> len(my_float)
Traceback (most recent call last):
  File "", line 1, in 
    len(my_float)
TypeError: object of type 'float' has no len()

Để bạn gọi len(obj), ràng buộc thực sự duy nhất trên obj là nó phải xác định phương thức .__len__(). Mặt khác, đối tượng có thể có các loại khác nhau như str,

char sz[] = "abcdefg";
int *i = (int *)sz;
0,
char sz[] = "abcdefg";
int *i = (int *)sz;
1 hoặc
char sz[] = "abcdefg";
int *i = (int *)sz;
2.

Có một số vấn đề quan trọng mà tôi nghĩ rằng tất cả các câu trả lời hiện có đã bỏ lỡ.


Gõ yếu có nghĩa là cho phép truy cập vào đại 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 tôi muốn sử dụng nó như một con trỏ tới số nguyên:

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

Trên một nền tảng nhỏ với số nguyên 32 bit, điều này làm cho

char sz[] = "abcdefg";
int *i = (int *)sz;
3 thành một mảng các số
char sz[] = "abcdefg";
int *i = (int *)sz;
4 và
char sz[] = "abcdefg";
int *i = (int *)sz;
5. Trên thực tế, bạn thậm chí có thể tự đúc con trỏ vào 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 cứ đâ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 để tôi chỉ có thể ghi đè lên bộ nhớ quy trình của riêng mình, nhưng không có gì về C chính nó cung cấp sự bảo vệ như vậy, như bất kỳ ai từng mã hóa, nói, Mac OS hoặc Win16 cổ điển có thể nói với bạn.

Lisp truyền thống cho phép các loại tặc tương tự; Trên một số nền tảng, các tế bào nổi hai từ và các tế bào là cùng một loại và bạn chỉ có thể chuyển một từ cho một chức năng mong đợi cái kia và nó sẽ "hoạt động".

Hầu hết các ngôn ngữ ngày nay không hoàn toàn yếu như C và Lisp, nhưng nhiều ngôn ngữ vẫn còn bị rò rỉ. Ví dụ: bất kỳ ngôn ngữ oo nào có "downcast",* đó là một loại rò rỉ: 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à, "khi toàn bộ điểm của một hệ thống loại là trình biên dịch luôn có đủ thông tin để biết những gì an toàn.

* Một DOWCAST được kiểm tra không làm cho hệ thống loại của ngôn ngữ trở nên yếu hơn chỉ vì nó di chuyển kiểm tra đến thời gian chạy. Nếu đã làm, thì đa hình phân nhóm (còn gọi là các cuộc gọi chức năng ảo hoặc đầy đủ động lực học) sẽ là sự vi phạm tương tự của hệ thống loại và tôi không nghĩ 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ó như một số nguyên.* Nhưng điều đáng chú ý là trong cpython (và tương tự đối với nhiều phiên dịch khác cho nhiều ngôn ngữ), nếu bạn thực sự kiên trì có thể sử dụng

char sz[] = "abcdefg";
int *i = (int *)sz;
6 để tải lên
char sz[] = "abcdefg";
int *i = (int *)sz;
7, chuyển
char sz[] = "abcdefg";
int *i = (int *)sz;
8 của một đối tượng lên
char sz[] = "abcdefg";
int *i = (int *)sz;
9 và buộc hệ thống loại bị rò rỉ. Liệu điều này có làm cho hệ thống loại yếu hay không phụ 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 thực hiện hộp cát thực thi bị hạn chế bằng ngôn ngữ để đảm bảo bảo mật, bạn phải đối phó với các loại thoát khỏi các loại này

* Bạn có thể sử dụng một chức năng như

intptr_t i = (intptr_t)&sz;
0 để đọc các byte và xây dựng một int mới về "Làm thế nào C sẽ đại diện cho các byte này", nhưng điều đó rõ ràng không bị rò rỉ; Ngay cả Haskell cũng cho phép điều đó.


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

Mỗi ngôn ngữ, thậm chí Haskell, có các chức năng để, nói, chuyển đổi một số nguyên thành một chuỗi hoặc một 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. Điều này chắc chắn có thể dẫn đến các lỗi với, ví dụ, tràn bất ngờ, nhưng chúng không phải là loại lỗi mà bạn nhận được từ một hệ thống loại yếu. Và C không thực sự là bất kỳ yếu hơn ở đây; Bạn có thể thêm một int và một float trong haskell, hoặc thậm chí nối một chiếc phao vào một chuỗi, bạn chỉ cần làm điều đó một cách rõ ràng hơn.

Và với các ngôn ngữ năng động, điều này khá âm u. Không có thứ gọi là "một chức năng muốn nổi" trong Python hoặc Perl. Nhưng có những chức năng quá tải làm những việc khác nhau với các loại khác nhau và có một ý nghĩa trực quan mạnh mẽ rằng, ví dụ, thêm một chuỗi vào một thứ khác là "một hàm muốn 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 (

intptr_t i = (intptr_t)&sz;
3 cung cấp cho bạn
intptr_t i = (intptr_t)&sz;
4), trong khi Python thực hiện ít hơn rất nhiều (
intptr_t i = (intptr_t)&sz;
3 làm tăng một ngoại lệ, nhưng
intptr_t i = (intptr_t)&sz;
6 không cung cấp cho bạn ____ 27*). Thật khó để đặt ý nghĩa đó vào 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 lấy một chuỗi và int, khi rõ ràng có các chức năng khác, như lập chỉ mục, làm gì?

* Trên thực tế, trong Python hiện đại, điều đó có thể được giải thích về mặt phân nhóm 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 ví dụ của loại chuỗi trong Perl hoặc JavaScript, mặc dù trong TCL, nhưng thực tế, 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ề việc gõ "mạnh" so với "yếu", trong đó "mạnh mẽ" 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 một loại là số, chuỗi, danh sách loại này hoặc bản đồ từ chuỗi đến loại này, đây là một cách hoàn toàn để thể hiện bất cứ điều gì có thể được giải mã từ JSON. Không có cách nào để xác định một loại như vậy trong Java. Nhưng ít nhất Java có các loại tham số (chung), vì vậy bạn có thể viết một hàm lấy một danh sách T và biết rằng các yếu tố thuộc loại T; Các ngôn ngữ khác, như Java sớm, buộc bạn phải sử dụng một danh sách đối tượng và downcast. Nhưng ít nhất Java cho phép bạn tạo các loại mới với các phương pháp của riêng họ; C chỉ cho phép bạn tạo cấu trúc. Và BCPL thậm chí không có điều đó. Và như vậy, xuống để 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 loại 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 quang phổ đó ở đâu? Đó là một chút khó khăn. Trong nhiều trường hợp, 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ể; Chắc chắn, các lỗi bị bắt trong thời gian chạy thay vì thời gian biên dịch, nhưng chúng vẫn bị bắt. Tuy nhiên, có những trường hợp gõ vịt không đủ. Ví dụ: trong Haskell, bạn có thể nói rằng một danh sách số trống của INTS là danh sách các INT, vì vậy bạn có thể quyết định rằng việc giảm

intptr_t i = (intptr_t)&sz;
8 trong danh sách đó sẽ trả về 0*; Trong Python, một danh sách trống là một danh sách trống; Không có thông tin loại nào để giúp bạn quyết định giảm
intptr_t i = (intptr_t)&sz;
8 so với nó nên làm.

* Trên thực tế, Haskell không cho phép bạn làm điều này; Nếu bạn gọi hàm giảm không lấy giá trị bắt đầu trong danh sách trống, bạn sẽ gặp lỗi. 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.

Python sử dụng gõ nào?

Python sử dụng gõ động, trong đó các biến, tham số và giá trị trả về của hàm có thể là bất kỳ loại nào. Ngoài ra, các loại biến có thể thay đổi trong khi chương trình chạy. Nói chung, gõ động giúp dễ dàng lập trình và gây ra các lỗi bất ngờ mà bạn chỉ có thể khám phá cho đến khi chương trình chạy.dynamic typing, in which variables, parameters, and return values of a function can be any type. Also, the types of variables can change while the program runs. Generally, dynamic typing makes it easy to program and causes unexpected errors that you can only discover until the program runs.

Python có gõ tĩnh không?

Python sẽ luôn luôn là một ngôn ngữ được đánh máy động. Tuy nhiên, PEP 484 đã giới thiệu các gợi ý loại, điều này cũng có thể thực hiện kiểm tra loại tĩnh của mã python. Không giống như cách các loại hoạt động trong hầu hết các ngôn ngữ được gõ tĩnh khác, bản thân các gợi ý không khiến Python thực thi các loại.. However, PEP 484 introduced type hints, which make it possible to also do static type checking of Python code. Unlike how types work in most other statically typed languages, type hints by themselves don't cause Python to enforce types.

Gõ động là gì trong Python?

Python là một ngôn ngữ được đánh máy động. Điều này có nghĩa là trình thông dịch Python chỉ kiểm tra loại khi mã chạy và loại biến được phép thay đổi trong suốt vòng đời của nó.

Tại sao Python là một ngôn ngữ được đánh máy mạnh mẽ?

Python được gõ mạnh khi trình thông dịch theo dõi tất cả các loại biến. Nó cũng rất năng động vì nó hiếm khi sử dụng những gì nó biết để hạn chế sử dụng biến. Trong Python, trách nhiệm của chương trình là sử dụng các hàm tích hợp như isinstance () và issubclass () để kiểm tra các loại biến và sử dụng chính xác.the interpreter keeps track of all variables types. It's also very dynamic as it rarely uses what it knows to limit variable usage. In Python, it's the program's responsibility to use built-in functions like isinstance() and issubclass() to test variable types and correct usage.